Java编程思想第四版阅读笔记(15章 泛型)

简单泛型

Java编程思想第四版阅读笔记(15章 泛型)_第1张图片

package generics;

class Automobile {}

public class Holder1 {
    private Automobile a;	// Holder1 只能持有 Automobile 这一种类型的对象
    public Holder1 (Automobile a) {
        this.a = a;
    }
    public Automobile get() {
        return a;
    }
}

package generics;

public class Holder2 {
    private Object a;   // Holder2 可以持有任意类型的对象
    public Holder2(Object a) {
        this.a = a;
    }
    public void set(Object a) {
        this.a = a;
    }
    public Object get() {
        return a;
    }

    public static void main(String[] args) {
        Holder2 holder2 = new Holder2(new Automobile());
        Automobile a = (Automobile) holder2.get();  // 需要强制转换,否则编译错误

        holder2.set("Not an automobile");
        String s = (String) holder2.get();   // 需要强制转换,否则编译错误

        holder2.set(1);
        Integer i = (Integer) holder2.get();     // 需要强制转换,否则编译错误
    }
}

package generics;

public class Holder3<T> {
    private T a;    // 可指定任何特定类型,指定后只能使用该种类型
    public Holder3(T a) {
        this.a = a;
    }
    public void set(T a){
        this.a = a;
    }
    public T get() {
        return a;
    }

    public static void main(String[] args) {
        Holder3<Automobile> holder3 = new Holder3<Automobile>(new Automobile());
        Automobile a = holder3.get();   // 无需强制转型
//        holder3.set(1); // Error
//        holder3.set("Not an automobile"); // Error
    }
}

一个元组类

将一组对象存储在一个对象中,只读不能添加。一次方法调用可返回多个对象。
Java编程思想第四版阅读笔记(15章 泛型)_第2张图片

package tuple;

public class TwoTuple<A, B> {
    public final A a;   // final 保证安全(虽是 public)
    public final B b;   // final 保证安全(虽是 public)
    public TwoTuple(A a, B b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public String toString() {
        return "(" + a + ", " + b + ")";
    }
}

package tuple;

public class ThreeTuple<A, B, C> extends TwoTuple<A, B> {   // 增加泛型参数非常简单,如是
    public final C c;
    public ThreeTuple(A a, B b, C c) {
        super(a, b);
        this.c = c;
    }

    @Override
    public String toString() {
        return "(" + a + ", " + b + ", " + c + ")";
    }
}

package tuple;

class Amphibian {}

public class TupleTest {

    public static TwoTuple<String, Integer> f1() {
        return new TwoTuple<String, Integer>("Java", 666);
    }
    public static ThreeTuple<Amphibian, String, Double> f2() {
        return new ThreeTuple<Amphibian, String, Double>(new Amphibian(), "Java", 99.9);
    }

    public static void main(String[] args) {
        System.out.println(f1());
        System.out.println(f2());
    }
}

Java编程思想第四版阅读笔记(15章 泛型)_第3张图片

一个堆栈类

利用链表手动实现一个栈。

package generics;

public class LinkedStack<T> {

    private static class Node<U> {  // 链表形式实现
        U item;
        Node<U> next;
        Node() {
            item = null;
            next = null;
        }
        Node(U item, Node<U> next) {
            this.item = item;
            this.next = next;
        }
        boolean end() {
            return item == null && next == null;
        }
    }
    private Node<T> top = new Node<T>();    // End sentinel

    public void push(T item) {
        top = new Node<T>(item, top);
    }
    public T pop() {
        T result = top.item;
        if(!top.end()) {
            top = top.next;
        }
        return result;
    }

    public static void main(String[] args) {
        LinkedStack<String> linkedStack = new LinkedStack<String >();
        for(String s : "a b c".split(" ")) {
            linkedStack.push(s);
        }
        String s;
        while ((s = linkedStack.pop()) != null) {
            System.out.println(s);
        }
    }
}

一个 RandomList 类

从一个泛型列表中随机选择一个元素。

package generics;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class RandomList<T> {
    private List<T> list = new ArrayList<T>();
    private Random random = new Random(47);
    public T select() {
        return list.get(random.nextInt(list.size()));
    }
    public void add(T item) {
        list.add(item);
    }

    public static void main(String[] args) {
        RandomList<String> randomList = new RandomList<String>();
        for(String s : "The quick brown fox jumped over the lazy brown dog".split(" ")) {
            randomList.add(s);
        }
        for(int i = 0; i < 10; i++) {
            System.out.print(randomList.select() + " ");
        }
    }
}

在这里插入图片描述

泛型接口

package genericsInterface;

public interface Generator<T> {
    T next();
}

随意生成某种 Coffee 案例

Java编程思想第四版阅读笔记(15章 泛型)_第4张图片

package genericsInterface.coffee;

public class Coffee {
    private static long counter = 0;
    private final long id = counter++;

    @Override
    public String toString() {
        return getClass().getSimpleName() + "(id=" + id + ")";
    }
}

package genericsInterface.coffee;

public class Coffee1 extends Coffee { }

package genericsInterface.coffee;

public class Coffee2 extends Coffee { }

package genericsInterface.coffee;

public class Coffee3 extends Coffee { }

package genericsInterface.coffee;

public class Coffee4 extends Coffee { }

package genericsInterface.coffee;

public class Coffee5 extends Coffee { }

package genericsInterface.coffee;

import genericsInterface.Generator;

import java.util.Iterator;
import java.util.Random;

public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
    // 方式一(利用 Generator 手动实现)
    private Class[] types = {Coffee1.class, Coffee2.class, Coffee3.class, Coffee4.class, Coffee5.class};
    private static Random random = new Random(47);
    public CoffeeGenerator() {}

    // 方式二(迭代)
    private int size = 0;
    public CoffeeGenerator(int size) {
        this.size = size;
    }


    // 方式一
    @Override
    public Coffee next() {
        try {
            return (Coffee) types[random.nextInt(types.length)].newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    // 方式二
    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeIterator();
    }
    class CoffeeIterator implements Iterator<Coffee> {

        int count = size;

        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Coffee next() {
            count--;
            return CoffeeGenerator.this.next();
        }
    }

    public static void main(String[] args) {
        CoffeeGenerator coffeeGenerator = new CoffeeGenerator();    // 方式一
        for(int i = 0; i < 5; i++) {    // 随机生成 5 个 Coffee
            System.out.print(coffeeGenerator.next() + " ");
        }
        System.out.println();

        for(Coffee coffee : new CoffeeGenerator(5)) {
            System.out.print(coffee + " ");
        }
    }
}

在这里插入图片描述

生成 Fibonacci 数案例

Java编程思想第四版阅读笔记(15章 泛型)_第5张图片

package genericsInterface.fibonacci;

import genericsInterface.Generator;


// Generator 对 Fibonacci 的应用
public class Fibonacci implements Generator<Integer> {

    private int n = 0;

    private int fib(int n) {
        if(n < 2) {
            return 1;
        }
        return fib(n-2) + fib(n-1);
    }

    @Override
    public Integer next() {
        return fib(n++);
    }

    public static void main(String[] args) {
        Fibonacci fibonacci = new Fibonacci();
        for(int i = 0; i < 18; i++) {
            System.out.print(fibonacci.next() + " ");
        }
    }
}

在这里插入图片描述

package genericsInterface.fibonacci;

import java.util.Iterator;

// 生成可迭代的 Fibonacci (适配器模式,继承方式实现)
public class IterableFibonacci extends  Fibonacci implements Iterable<Integer>{

    private int total;
    public IterableFibonacci(int total) {
        this.total = total;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return total > 0;
            }

            @Override
            public Integer next() {
                total--;
                return IterableFibonacci.this.next();
            }
        };
    }

    public static void main(String[] args) {
        for(int x : new IterableFibonacci(18)) {
            System.out.print(x + " ");
        }
    }
}

在这里插入图片描述

泛型方法

定义泛型方法,只需要把泛型参数列表置于返回值之前。static 方法无法访问泛型类的类型参数,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。

package genericsMethod;

public class GenericMethods {
    public <T> void f(T x) {
        System.out.println(x.getClass().getSimpleName());
    }

    public static void main(String[] args) {
        GenericMethods genericMethods = new GenericMethods();
        genericMethods.f("");

        genericMethods.f('c');
        genericMethods.f(false);
        genericMethods.f((byte)1);
        genericMethods.f((short)1);
        genericMethods.f(1);
        genericMethods.f(1L);
        genericMethods.f(1.0f);
        genericMethods.f(1.0);

        genericMethods.f(genericMethods);
    }
}

Java编程思想第四版阅读笔记(15章 泛型)_第6张图片

泛型类型参数推断

package genericsMethod;

import com.sun.org.apache.xerces.internal.xs.StringList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 利用 New 类的各方法创建 Map, List 的时候右侧不必再指定类型。其实也就减轻了多写个泛型参数的工作!!
 * List list = list();  // 赋值号后不再指定 String 类型,可根据赋值号前的泛型参数完成类型推断
 * List list = new ArrayList(); // 赋值号后的 String 可省去,其实 JDK 8 已经实现过了。。
 *
 */

public class New {
    public static <K, V> Map<K, V> map() {
        return new HashMap<K, V>();
    }
    public static <T> List<T> list() {
        return new ArrayList<T>();
    }

    public static void f(List<String> s) {}

    public static void main(String[] args) {
        Map<String, List<String>> map = New.map(); // 参数类型推断
        List<String> list = New.list(); // 参数类型推断
        f(New.list());  // 笔者使用的是 JDK 8,这行代码不报错,说明作为参数,编译器也可完成参数类型推断
        f(New.<String>list()); // 显示指定也行。。(JDK 5 的时候,编译器无法完成类型推断,必须显示指定)
    }
}

可变参数的泛型方法

package genericsMethod;

import java.util.ArrayList;
import java.util.List;

public class GenericsVariableArgs {
    public static <T> List<T> makeList(T... args) {
        List<T> result = new ArrayList<>();
        for(T arg : args) {
            result.add(arg);
        }
        return result;
    }

    public static void main(String[] args) {
        List<String> list = makeList("a", "b", "c");
        System.out.println(list);
    }
}

用于 Generator 的泛型方法

Java编程思想第四版阅读笔记(15章 泛型)_第7张图片

package genericsMethod;

import genericsInterface.Generator;
import genericsInterface.coffee.Coffee;
import genericsInterface.coffee.CoffeeGenerator;
import genericsInterface.fibonacci.Fibonacci;

import java.util.ArrayList;
import java.util.Collection;

public class Generators {
    public static <T> Collection<T> fill(Collection<T> collection, Generator<T> generator, int n) {
        for(int i = 0; i < n; i++) {
            collection.add(generator.next());
        }
        return collection;
    }

    public static void main(String[] args) {
        Collection<Coffee> coffeeCollection = fill(new ArrayList<>(), new CoffeeGenerator(), 4);
        for(Coffee coffee : coffeeCollection) {
            System.out.print(coffee + " ");
        }
        System.out.println();

        Collection<Integer> fibonacciCollection = fill(new ArrayList<>(), new Fibonacci(), 12);
        for(Integer integer : fibonacciCollection) {
            System.out.print(integer + " ");
        }
        System.out.println();
    }
}

Java编程思想第四版阅读笔记(15章 泛型)_第8张图片

一个通用的 Generator

Java编程思想第四版阅读笔记(15章 泛型)_第9张图片

package genericsMethod;

import genericsInterface.Generator;

public class BasicGenerator<T> implements Generator<T> {

    private Class<T> type;
    private BasicGenerator(Class<T> type) {
        this.type = type;
    }

    public static <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<T>(type);
    }

    @Override
    public T next() {
        try {
            return type.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

package genericsMethod;

public class CountedObject {
    private static long counter = 0;
    private long id = counter++;
    public long id() {
        return id;
    }
    @Override
    public String toString() {
        return "CountedObject" + "(id=" + id + ")";
    }
}

package genericsMethod;

import genericsInterface.Generator;

public class BasicGeneratorDemo {
    public static void main(String[] args) {
        Generator<CountedObject> generator = BasicGenerator.create(CountedObject.class);
        for(int i = 0; i < 5; i++) {
            System.out.println(generator.next());
        }
    }
}

简化元组的使用

Java编程思想第四版阅读笔记(15章 泛型)_第10张图片

package tuple;

public class Tuple {
    public static <A, B> TwoTuple<A, B> tuple(A a, B b) {
        return new TwoTuple<A, B>(a, b);
    }
    public static <A, B, C> ThreeTuple<A, B, C> tuple(A a, B b, C c) {
        return new ThreeTuple<A, B, C>(a, b, c);
    }

    public static void main(String[] args) {
        System.out.println(tuple("a", 1.0));
        System.out.println(tuple("a", 6, 6.0));
    }
}

一个 Set 实用工具

package genericsMethod;

import java.util.HashSet;
import java.util.Set;

public class Sets {
    public static <T> Set<T> union(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<T>(a);
        result.addAll(b);
        return result;
    }
    public static <T> Set<T> intersection(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<T>(a);
        result.retainAll(b);
        return result;
    }
    public static <T> Set<T> difference(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<T>(a);
        result.removeAll(b);
        return result;
    }
    // everything not in the intersection
    public static <T> Set<T> complement(Set<T> a, Set<T> b) {
        return union(difference(a, b), difference(b, a)); // difference(union(a, b), intersection(a, b))
    }

    public static void main(String[] args) {
        Set<Integer> a = new HashSet<>();
        Set<Integer> b = new HashSet<>();
        for(int i = 0; i < 5; i++) {
            a.add(i);
            b.add(i+2);
        }
        System.out.println("a: " + a);
        System.out.println("b: " + b);
        System.out.println("union(a, b): " + union(a, b));
        System.out.println("intersection(a, b): " + intersection(a, b));
        System.out.println("difference(a, b): " + difference(a, b));
        System.out.println("complement(a, b): " + complement(a, b));
    }
}

Java编程思想第四版阅读笔记(15章 泛型)_第11张图片

匿名内部类

package generics;

import genericsInterface.Generator;
import genericsMethod.Generators;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

class Customer{
    private static long counter = 0;
    private long id = counter++;
    private Customer() {}

    @Override
    public String toString() {
        return "Customer" + " " + id;
    }

    public static Generator<Customer> generator() {
        return new Generator<Customer>() {
            @Override
            public Customer next() {
                return new Customer();
            }
        };
    }
}

class Teller{
    private static long counter = 0;
    private long id = counter++;
    private Teller() {}

    @Override
    public String toString() {
        return "Teller" + " " + id;
    }

    public static Generator<Teller> generator() {
        return new Generator<Teller>() {
            @Override
            public Teller next() {
                return new Teller();
            }
        };
    }
}

public class BankTeller {

    public static void serve(Teller teller, Customer customer) {
        System.out.println(teller + " serves " + customer);
    }

    public static void main(String[] args) {
        Random random = new Random(47);
        List<Customer> customers = new ArrayList<>();
        Generators.fill(customers, Customer.generator(), 9);
        List<Teller> tellers = new ArrayList<>();
        Generators.fill(tellers, Teller.generator(), 3);

        for(Customer customer : customers) {
            serve(tellers.get(random.nextInt(tellers.size())), customer);
        }
    }
}

构建复杂模型

package generics;

import tuple.TupleTest;
import tuple.TwoTuple;

import java.util.ArrayList;

// ComplexModel 具备了 ArrayList 的能力,存储的元素是 TwoTuple 类型
public class ComplexModel<A, B> extends ArrayList<TwoTuple<A, B>> {
    public static void main(String[] args) {
        ComplexModel<String, Integer> complexModel = new ComplexModel<String, Integer>();
        complexModel.add(TupleTest.f1());
        complexModel.add(TupleTest.f1());

        for(TwoTuple<String, Integer> twoTuple : complexModel) {
            System.out.println(twoTuple);
        }
    }
}

擦除

泛型是在 JDK 5 中才引入的,之前的 Java 版本没有泛型,为了兼容以前的代码,运行时泛型擦除了,擦除成边界原生类型。注意 new 操作。。

package typeErased;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * 泛型擦除
 * new ArrayList().getClass() 和 new ArrayList().getClass() 运行时泛型被擦除,都成了 ArrayList.class
 */
public class TypeErasedEquivalence {
    public static void main(String[] args) {
        Class c1 = new ArrayList<String>().getClass();
        Class c2 = new ArrayList<Integer>().getClass();
        System.out.println(c1 == c2);   // true
        System.out.println(c1 == ArrayList.class);  // true
        // 拿到的仅仅是泛型参数占位符的标识符(见 ArrayList 源码,并拿不到真正的 String 这个类型参数)
        System.out.println(Arrays.toString(c1.getTypeParameters()));    // [E]
        System.out.println(Arrays.toString(c2.getTypeParameters()));    // [E]

    }
}

package typeErased;

import java.lang.reflect.Array;
import java.util.Arrays;

public class ArrayMaker<T> {
    private Class<T> kind;
    public ArrayMaker(Class<T> kind) {
        this.kind = kind;
    }

    public Class<T> getKind() {
        return kind;
    }

    T[] create(int size) {
        return (T[]) Array.newInstance(kind, size);
    }

    public static void main(String[] args) {
        ArrayMaker<String> stringArrayMaker = new ArrayMaker<>(String.class);   // 运行时 stringArrayMaker 泛型 String 擦除
        // System.out.println(Arrays.toString(stringArrayMaker.getKind().getTypeParameters()));    // [],kind 的泛型已经擦除
        String[] stringArray = stringArrayMaker.create(9);  // create 方法传入的 kind 的泛型已经擦除。。
        System.out.println(Arrays.toString(stringArray));   // [null, null, null, null, null, null, null, null, null]
    }
}

package typeErased;

import java.util.ArrayList;
import java.util.List;

public class FilledListMaker<T> {
    List<T> create(T t, int n) {
        List<T> result = new ArrayList<T>();
        for(int i = 0; i < n; i++) {
            result.add(t);
        }
        return result;
    }

    public static void main(String[] args) {
        FilledListMaker<String> stringMaker = new FilledListMaker<String>();    // 类型参数 String 被擦除
        List<String> list = stringMaker.create("hah", 9);   // 在方法返回之前插入数据,可生成 String 类型的 List
                // 对象进入和离开方法的地点(边界)正是编译器在编译期执行类型检查并插入转型代码的地方
        System.out.println(list);
    }
}

利用 javap -c 反编译 SimpleHolder 和 GenericHolder,可知道在方法的入口和出口处正是编译器在编译期执行类型检查并插入转型代码的地方!注意查看 set 和 get 方法的反编译代码标号是不连续的,在 putfield 和 getfield 之后都少两行,说明编译器进行额外的类型检查或转型。。

package typeErased;

public class SimpleHolder {
    private Object obj;
    public void set(Object obj) {
        this.obj = obj;
    }

    public Object get() {
        return obj;
    }

    public static void main(String[] args) {
        SimpleHolder holder = new SimpleHolder();
        holder.set("item");
        String s = (String) holder.get();  // 反编译,此处 checkcast,转型
    }
}

package typeErased;

public class GenericHolder<T> {
    private T obj;
    public void set(T obj) {
        this.obj = obj;
    }
    public T get() {
        return obj;
    }

    public static void main(String[] args) {
        GenericHolder<String> holder = new GenericHolder<String>();
        holder.set("item");
        String s = holder.get();    // 反编译,此处 checkcast,转型
    }
}

Compiled from "GenericHolder.java"
public class typeErased.GenericHolder<T> {
  public typeErased.GenericHolder();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public void set(T);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field obj:Ljava/lang/Object;
       5: return

  public T get();
    Code:
       0: aload_0
       1: getfield      #2                  // Field obj:Ljava/lang/Object;
       4: areturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class typeErased/GenericHolder
       3: dup
       4: invokespecial #4                  // Method "":()V
       7: astore_1
       8: aload_1
       9: ldc           #5                  // String item
      11: invokevirtual #6                  // Method set:(Ljava/lang/Object;)V
      14: aload_1
      15: invokevirtual #7                  // Method get:()Ljava/lang/Object;
      18: checkcast     #8                  // class java/lang/String
      21: astore_2
      22: return
}

擦除的补偿

package typeErased;

class Building {}
class House extends Building {}

public class ClassTypeCapture<T> {
    Class<T> kind;
    public ClassTypeCapture(Class<T> kind) {
        this.kind = kind;
    }
    public boolean f(Object arg) {
//        if(arg instanceof T){}  // 运行时需要确切类型,然而类型已经擦除(Error)
//        T var = new T();    // 运行时需要确切类型(Error),然而类型已经擦除(Error)
//        T[] array = new T[100]; // 运行时需要确切类型(Error),然而类型已经擦除(Error)
//        T[] array2 = (T[])new Object[100]; // Unchecked cast(警告)
        return kind.isInstance(arg);    // isInstance 竟然可以!!!书上说编译器确保可以。。
    }

    public static void main(String[] args) {
        ClassTypeCapture<Building> classTypeCapture = new ClassTypeCapture<Building>(Building.class);
        System.out.println(classTypeCapture.f(new Building()));
        System.out.println(classTypeCapture.f(new House()));

        ClassTypeCapture<House> classTypeCapture2 = new ClassTypeCapture<House>(House.class);
        System.out.println(classTypeCapture2.f(new Building()));
        System.out.println(classTypeCapture2.f(new House()));
    }
}

创建类型实例

package typeErased;

class ClassAsFactory<T> {
    T x;
    public ClassAsFactory(Class<T> kind) {
        try {
            x = kind.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

class Employee {}

public class InstantiateGenericType {
    public static void main(String[] args) {
        ClassAsFactory<Employee> fe = new ClassAsFactory<Employee>(Employee.class);
        System.out.println("ClassAsFactory succeeded");
        try {
            ClassAsFactory<Integer> fi = new ClassAsFactory<Integer>(Integer.class);
        } catch (Exception e) { // 该异常在编译期捕获不到,在运行期才捕获到,Sun 公司不赞成这种做法
            System.out.println("ClassAsFactory failed");   // Integer 没有无参构造器,创建 fi 失败
        }
    }
}

针对上面代码问题,书上提供了两种改进方式,工厂和模板方法。

package typeErased;

// 工厂

interface FactoryI<T> {
    T create();
}

class Foo2<T> {
    private T x;
    public <F extends FactoryI<T>> Foo2(F factory) {
        x = factory.create();
    }
}

class IntegerFactory implements FactoryI<Integer> {
    @Override
    public Integer create() {
        return new Integer(0);
    }
}

class Widget {
    public static class Factory implements FactoryI<Widget> {

        @Override
        public Widget create() {
            return new Widget();
        }
    }
}

public class FactoryConstraint {
    public static void main(String[] args) {
        new Foo2<Integer>(new IntegerFactory());
        new Foo2<Widget>(new Widget.Factory());
    }
}

package typeErased;

// 模板方法

abstract class GenericWithCreate<T> {
    final T element;
    GenericWithCreate() {element = create();}
    abstract T create();
}

class X {}

class Creator extends GenericWithCreate<X> {

    @Override
    X create() {
        return new X();
    }

    void f() {
        System.out.println(element.getClass().getSimpleName());
    }
}

public class CreatorGeneric {
    public static void main(String[] args) {
        Creator creator = new Creator();
        creator.f();
    }
}

泛型数组

package typeErased;

import java.util.ArrayList;
import java.util.List;

// 由于不能创建泛型数组 T[] a = new T[size],一般的解决方案是,需要创建泛型数组的时候就用 ArrayList
public class ListOfGenerics<T> {
    private List<T> array = new ArrayList<T>();
    public void add(T item) {
        array.add(item);
    }
    public T get(int index) {
        return array.get(index);
    }

    public static void main(String[] args) {
        ListOfGenerics<Integer> list = new ListOfGenerics<Integer>();
//        list.add("1");    // 编译检查类型不匹配
        list.add(1);
        System.out.println(list.get(0));    // 1
    }
}

package typeErased;

public class GenericArray<T> {
    private T[] array;

    public GenericArray(int size) {
        array = (T[])new Object[size];
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) {
        return array[index];
    }
    public T[] rep() {
        return array;
    }

    public static void main(String[] args) {
        GenericArray<Integer> genericArray = new GenericArray<Integer>(10);

        for(int i = 0; i < 10; i++) {
            genericArray.put(i, i);
        }
        for(int i = 0; i < 10; i++) {
            System.out.print(genericArray.get(i) + " ");
        }

//        Integer[] integers = genericArray.rep();    // 编译期正确,运行时泛型擦除导致 Object[] 转 Integer[] 导致错误
        Object[] objects = genericArray.rep();
    }
}

package typeErased;

public class GenericArray2<T> {
    private Object[] array;
    public GenericArray2(int size) {
        array = new Object[size];
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) {
        return (T) array[index];
    }
    public T[] rep() {
        return (T[])array;
    }

    public static void main(String[] args) {
        GenericArray2<Integer> genericArray2 = new GenericArray2<Integer>(10);
        for(int i = 0; i < 10; i++) {
            genericArray2.put(i, i);
        }
        for(int i = 0; i < 10; i++) {
            System.out.print(genericArray2.get(i) + " ");
        }
        System.out.println();

        // 运行时异常 ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
//        Integer[] integers = genericArray2.rep();
    }
}

package typeErased;

import java.lang.reflect.Array;

public class GenericArrayWithTypeToken<T> {
    private T[] array;

    public GenericArrayWithTypeToken(Class<T> type, int size) {
        array = (T[]) Array.newInstance(type, size);
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) {
        return array[index];
    }
    public T[] rep() {
        return array;
    }

    public static void main(String[] args) {
        GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<Integer>(Integer.class, 10);
        for(int i = 0; i < 10; i++) {
            gai.put(i, i);
        }
        for(int i = 0; i < 10; i++) {
            System.out.print(gai.get(i) + " ");
        }
        System.out.println();

        Integer[] integers = gai.rep(); // 没问题了。。
    }
}

边界

package bound;

import java.awt.*;

interface HasColor {
    java.awt.Color getColor();
}

class Dimension {
    public int x, y, z;
}

interface Weight {
    int weight();
}

class Solid<T extends Dimension & HasColor & Weight>{
    T item;
    Solid(T item) {
        this.item = item;
    }
    T getItem() {
        return this.item;
    }

    java.awt.Color color() {
        return item.getColor(); // 泛型边界使得可以调用 HasColor 的方法
    }
    int getX() {
        return item.x;
    }
    int getY() {
        return item.y;
    }
    int getZ() {
        return item.z;
    }
    int weight() {
        return item.weight();
    }
}

class Bounded extends Dimension implements HasColor, Weight {

    @Override
    public Color getColor() {
        return null;
    }

    @Override
    public int weight() {
        return 0;
    }
}

public class BasicBounds {
    public static void main(String[] args) {
        Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
        System.out.println(solid.getItem());
        System.out.println(solid.color());
        System.out.println(solid.getX());
        System.out.println(solid.weight());
    }
}

通配符

package wildcard;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}

public class Wildcard {
    public static void main(String[] args) {
        Fruit[] fruits = new Apple[10];
        fruits[0] = new Apple();    // OK
        fruits[1] = new Jonathan(); // OK
//        fruits[2] = new Fruit();    // 编译期没问题,运行时出问题
//        fruits[2] = new Orange();   // 编译期没问题,运行时出问题





        // 虽说运行时泛型擦除都成 Object 了,但编译期就出错了啊(编译器认为泛型 Fruit 和 Apple 不一致)
//        List fruitList = new ArrayList();

        List<? extends Fruit> fruits2 = Arrays.asList(new Apple());
        // fruit2 里存放的是 Fruit 或其子类的对象,但却不确定到底是哪种,故不可进行 add 操作,但可 get 操作!
//        fruits2.add(new Apple());
//        fruits2.add(new Fruit());
        Fruit fruit = fruits2.get(0);   // We know that it returns at least Fruit.
        Apple apple = (Apple) fruit;
        fruits2.contains(apple);    // 参考 ArrayList 的 API,contains 的参数是 Object
        fruits2.indexOf(apple);     // 参考 ArrayList 的 API,indexOf 的参数是 Object
    }

    static void f(List<? super Apple> apples) {
        //  既然 ? 表示至少是 Apple 及其父类,那么往里面添加 Apple 及其子类就没问题啊,add 操作没问题。
        //  但是,get 操作就不行了,拿到的应该被 Apple 接收还是 Apple 的哪个父类接收,着不知道啊。。
        //  (可以和 ? extends 对比下!)
        apples.add(new Apple());
        apples.add(new Jonathan());
//        apples.add(new Fruit());  // 必须是 Apple 及其子类
//        Apple apple = apples.get(0);  // 不可 get 操作
    }
}

泛型使用相关问题

实现参数化接口

在这里插入图片描述

转型与警告

package problems;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class ClassCasting {
    // @SuppressWarnings("unchecked")
    public void f(String[] args) throws Exception{
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(args[0]));
        List<String> list = (List<String>) in.readObject(); // 不像书上说的。。强转是可以的
        List<String> list1 = List.class.cast(in.readObject());  // 也可也这么转型
    }
}

重载

Java编程思想第四版阅读笔记(15章 泛型)_第12张图片

基类劫持了接口

package problems;

//这样做,Cat 重复两次实现 Comparable,是错的
//class Cat extends ComparablePet implements Comparable {}

//这样做,对方法覆盖,可行
class Cat extends ComparablePet {
    @Override
    public int compareTo(ComparablePet cat) {
        return 0;
    }
}
//Dog 泛型参数与 ComparablePet 对应的 Comparable 的泛型相同,也可以
class Dog extends ComparablePet implements Comparable<ComparablePet>{
    @Override
    public int compareTo(ComparablePet dog) {
        return 0;
    }
}

public class ComparablePet implements Comparable<ComparablePet> {
    public int compareTo(ComparablePet comparablePet) {
        return 0;
    }
}

自限定的类型

古怪的循环泛型(CRG, curious recursive generic)

package selfBounded;

public class BasicHolder<T> {
    T element;
    void set(T arg) {
        element = arg;
    }
    T get() {
        return element;
    }
    void f() {
        System.out.println(element.getClass().getSimpleName());
    }
}

package selfBounded;

class Other{} // 下面的 BasicHolder 可换成 BasicHolder,并且 subType1.set(new Other()) 等
class SubType extends BasicHolder<SubType> {}

public class CRGWithBasicHolder {
    public static void main(String[] args) {
        SubType subType1 = new SubType();
        SubType subType2 = new SubType();

        subType1.set(subType2);
        subType1.f();   // SubType,得到确切的类型
        System.out.println(subType1.get() == subType2); // true
    }
}

自限定

package selfBounded;

class SelfBounded<T extends SelfBounded<T>> {
    T element;
    SelfBounded<T> set(T arg) {
        element = arg;
        return this;
    }
    T get() {
        return element;
    }
}

class A extends SelfBounded<A> {}
class B extends SelfBounded<A> {}

class D {}
//class E extends SelfBounded {} // D 不是 SelfBounded 的子类,不可作为 SelfBounded 的泛型参数

class F extends SelfBounded {} // 这不会报错,没强制一定用自限定

public class SelfBounding {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.set(a).get() == a);    // true
    }
}

参数协变(方法参数类型会随子类而变化)

package selfBounded;

class Base {}
class Derived extends Base {}

interface OrdinaryGetter {
    Base get();
}
interface DerivedGetter extends OrdinaryGetter {
    Derived get();  // 返回类型由父接口的 Base 变成现在的 Derived
}

class OrdinarySetter {
    void set(Base base){
        System.out.println("OrdinarySetter.set(Base)");
    }
}
class DerivedSetter extends OrdinarySetter {
    void set(Derived derived) { // 和父类的 set 方法是重载关系
        System.out.println("DerivedSetter.set(Derived)");
    }
}

public class OrdinaryArguments {
    void test(DerivedGetter derivedGetter) {
        Derived derived = derivedGetter.get();  //
    }
    public static void main(String[] args) {
        Base base = new Base();
        Derived derived = new Derived();
        DerivedSetter derivedSetter = new DerivedSetter();
        derivedSetter.set(derived); // DerivedSetter.set(Derived)
        derivedSetter.set(base);    // OrdinarySetter.set(Base)
    }
}

不使用自限定,一样完成重载,如下。

package selfBounded;

class GenericSetter<T> {
    void set(T arg) {
        System.out.println("GenericSetter.set(Base)");
    }
}
class DerivedGS extends GenericSetter<Base> {   // 普通的继承,并没自限定,实现父类 set(Base) 和 set(Derived) 重载
    void set(Derived derived) {
        System.out.println("DerivedGS.set(derived)");
    }
}

public class PlainGenericInheritance {
    public static void main(String[] args) {
        Base base = new Base();
        Derived derived = new Derived();
        DerivedGS derivedGS = new DerivedGS();
        derivedGS.set(derived); // DerivedGS.set(derived)
        derivedGS.set(base);    // GenericSetter.set(Base)
    }
}

package selfBounded;

interface SelfBoundSetter<T extends SelfBoundSetter<T>> {
    void set(T arg);
}
interface Setter extends SelfBoundSetter<Setter> {}

public class SelfBoundingAndCovariantArguments {
    void test(Setter setter1, Setter setter2, SelfBoundSetter selfBoundSetter) {
        setter1.set(setter2);
//        setter1.set(selfBoundSetter); // 编译错误,set 参数必须是 Setter 类型,不可是基类类型 SelfBoundSetter
    }
}

动态类型安全

Java SE5 以前的代码没有泛型,容器里面添加对象就有可能把狗对象添加到猫列表中,怎么检测这种问题呢?
Java编程思想第四版阅读笔记(15章 泛型)_第13张图片

异常

由于擦除的原因,将泛型应用于异常是非常受限的,书上的例子不再看。。

潜在类型机制

书中说到,Python 和 C++ 支持潜在类型机制,以下面的 Python 为例,perform 方法里的 anything 仅仅是一个标识符,并没限制传进来的对象的类型,但这里其实暗含了一个接口,如果传入的对象类型不正确,Python 在运行时报错,而 C++ 在编译期就报错。而在 Java 中就不能简单的用 anything,而是要实现一个接口,代码编写会繁杂些。
Java编程思想第四版阅读笔记(15章 泛型)_第14张图片
Java 中想实现类似的潜在类型机制的话,可以借助反射,查找传入对象的方法,如果没有对应的方法,就抛异常,否则就执行。。

附上工程代码

代码

你可能感兴趣的:(Java基础知识记)