第15章 泛型

第15章 泛型

泛型是在JDK5出现的,泛型实现了参数化类型的概念,使代码可以应用于多种类型。

15.2 简单泛型

促使泛型出现的原因之一就是创建容器类。

一个元组类库

元组:将一组对象直接打包存储于其中的一个单一对象,允许读取元素,不允许存放新元素。

@ToString
// 将两个对象打包于一个对象
class TwoTuple<A,B>{
    public final A a;
    public final B b;

    public TwoTuple(A a, B b) {
        this.a = a;
        this.b = b;
    }
}
/*
为什么要这么做?
通过声明public,客户端可以直接读取元素,声明final限制存放新元素,如果要存放新元素,需要重新创建对象
*/

// 通过继承实现更长的元组
@ToString
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;
    }
}

// 返回元组
public class Test {
    public static void main(String[] args) {
        System.out.println(f());
        System.out.println(e());
    }
    static TwoTuple<String,Integer> f(){
        return new TwoTuple<>("make",12);
    }
    static ThreeTuple<String,Integer,Integer> e(){
        return new ThreeTuple<>("make",22,175);
    }
}

一个栈类

class MyStack<T>{
    // 节点类(包含当前数据及上一级节点)
    private  class Node<U>{
        private U item;
        private Node<U> next;

        public Node(U item, Node<U> next) {
            this.item = item;
            this.next = next;
        }

        public boolean end(){
            // 当数据和节点同时为空时及到头了
            return item == null && next == null;
        }
    }
    // 创建末端哨兵
    private Node<T> top = new Node<>(null,null);

    public void push(T item){
        top = new Node<>(item,top);
    }

    public T pop(){
        T item = top.item;
        if (!top.end()){
            top = top.next;
        }
        return item;
    }
}

 public static void main(String[] args) {
        MyStack<Integer> stack = new MyStack<>();
        for (int i = 0; i < 5; i++) {
            stack.push(i);
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(stack.pop());
        }
    }

/*
4
3
2
1
0
null
null
null
null
null
*/

RandomList

class RandomList<T>{
    private List<T> list = new ArrayList<>();
    Random random = new Random();
    public void add(T t){
        list.add(t);
    }

    public T randomGet(){
        return list.get(random.nextInt(list.size()));
    }
}
  public static void main(String[] args) {
        RandomList<Integer> list = new RandomList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(list.randomGet());
        }
    }

15.3 泛型接口

class Coffee {
    // 计数,创建了多少对象
    private static Integer counter = 0;
    // 第n次创建的对象
    private final Integer id = counter++;

    @Override
    public String toString() {
        return "Coffee{" +
                "id=" + id +
                '}';
    }
}

class ACoffee extends Coffee{}
class BCoffee extends Coffee{}
class CCoffee extends Coffee{}

interface Factory<T>{
    T next();
}


class CoffeeFactory implements Factory<Coffee>,
        // 为了可以使用foreach
        Iterable<Coffee>{
    private Class[] classes = {ACoffee.class,BCoffee.class,Coffee.class};
    Random random = new Random();
    private Integer size = 0;

    public CoffeeFactory() {
    }
    // 如果要使用循环语句,必须有该方法,否则不知道何时停止
    public CoffeeFactory(Integer size) {
        this.size = size;
    }

    @SneakyThrows
    @Override
    public Coffee next() {
        return (Coffee) classes[random.nextInt(classes.length)].newInstance();
    }

    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeIterator();
    }

    // 迭代器
    private class CoffeeIterator implements Iterator<Coffee>{
        // 被迭代的数量
        private Integer count = size;
        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Coffee next() {
            // 被迭代一次,减去一次
            count--;
            // 获得调用该迭代器的next()方法
            return CoffeeFactory.this.next();
        }
    }
}

 public static void main(String[] args) {
        Factory<Coffee> factory = new CoffeeFactory();
        for (int i = 0; i < 5; i++) {
            System.out.println(factory.next());
        }
        for(Coffee coffee:new CoffeeFactory(5)){
            System.out.println(coffee);
        }
    }
/*
Coffee{id=0}
Coffee{id=1}
Coffee{id=2}
Coffee{id=3}
Coffee{id=4}
Coffee{id=5}
Coffee{id=6}
Coffee{id=7}
Coffee{id=8}
Coffee{id=9}
*/
// 创建斐波那契数列
//此处可以看到泛型不支持基本类型,但是由于自动装箱/拆箱,使用中不存在问题
class FibonacciFactory implements Factory<Integer>{
    private int num = 0;

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

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

// 为了使用循环语句,这里使用适配器模式
class FibonacciFactoryAdapter extends FibonacciFactory implements Iterable<Integer>{
    private int count =0;

    public FibonacciFactoryAdapter() {
    }

    public FibonacciFactoryAdapter(int count) {
        this.count = count;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int size = count;
            @Override
            public boolean hasNext() {
                return size>0;
            }

            @Override
            public Integer next() {
                size--;
                return FibonacciFactoryAdapter.this.next();
            }
        };
    }
}

  public static void main(String[] args) {
        Factory<Integer> factory = new FibonacciFactory();
        for (int i = 0; i < 5; i++) {
            System.out.println(factory.next());
        }

        for (Integer num:new FibonacciFactoryAdapter(5)){
            System.out.println(num);
        }
    }
/*
1
1
2
3
5
1
1
2
3
5
*/

15.4 泛型方法

在使用泛型方法即可解决问题时,不要使用泛型类。

static方法如果要使用泛型能力,必须使其成为泛型方法。

定义泛型方法:将泛型参数列表置于返回值之前。

public class Test {
    public static void main(String[] args) {
        f(1);
        f("a");
        f(11.2);
        f(new StringBuilder());
    }
    static <T> void f(T t){
        System.out.println(t.getClass().getSimpleName());
    }
}
/*
Integer
String
Double
StringBuilder
*/

15.4.1 杠杆利用类型参数推断

本节主要是为了实现泛型的类型推断,该功能在JDK1.7中已经引入。

15.4.2 可变参数和泛型方法

public class Test {
    public static void main(String[] args) {
        f(1,2,3);
        f("a","b","c");
    }
    static <T> void f(T ...args){
      List<T> list = new ArrayList<>();
        for (T arg : args) {
            list.add(arg);
        }
        System.out.println(list);
    }
}
/*
[1, 2, 3]
[a, b, c]
*/

15.4.3用于Factory的泛型方法

public class Test {
    public static void main(String[] args) {
        System.out.println(f(new ArrayList<>(), new CoffeeFactory(), 5));
    }
    
    static<T> Collection<T> f(Collection<T> collection,Factory<T> factory,int i){
        for (int j = 0; j < i; j++) {
            collection.add(factory.next());
        }
        return collection;
    }

}
/*
[Coffee{id=0}, Coffee{id=1}, Coffee{id=2}, Coffee{id=3}, Coffee{id=4}]
*/

15.4.4通用的工厂方法

class BasicFactory<T> implements Factory<T>{
    private Class<T> aClass;
    public BasicFactory(Class<T> aClass) {
        this.aClass = aClass;

    }
    @SneakyThrows
    @Override
    public T next() {
        return aClass.newInstance();
    }
}
 public static void main(String[] args) {
       Factory<Coffee> factory = new BasicFactory<>(Coffee.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(factory.next());
        }
    }

15.4.5简化元组的使用

public class Test {
    public static void main(String[] args) {
        // 没有指定接收变量的参数类型,默认为Object
        TwoTuple twoTuple = getTwoTuple(1,"a");
        Integer i = (Integer) twoTuple.a;
        ThreeTuple<String,Integer,Integer> threeTuple = getThreeTuple("make",12,111);
        String name = threeTuple.a;
    }
    
    static <A,B> TwoTuple<A,B> getTwoTuple(A a,B b){
        return new TwoTuple<>(a,b);
    }
    
    static <A,B,C> ThreeTuple<A,B,C> getThreeTuple(A a,B b,C c){
        return new ThreeTuple<>(a,b,c);
    }
}
15.4.6 一个Set实用工具
class Sets{

    // a和b的所有元素
    public static <T> Set<T> union(Set<T> a,Set<T> b){
        Set<T> set = new HashSet<>(a);
        set.addAll(b);
        return set;
    }

    // a和b的交集
    public static <T> Set<T> intersection(Set<T> a,Set<T> b){
        Set<T> set = new HashSet<>(a);
        set.retainAll(b);
        return set;
    }

    // a中去除b中的元素
    public static <T> Set<T> difference(Set<T> a,Set<T> b){
        Set<T> set = new HashSet<>(a);
        set.removeAll(b);
        return set;
    }

    // a和b的非交集元素
    public static <T> Set<T> complement(Set<T> a,Set<T> b){
        return difference(union(a,b),intersection(a, b));
    }
}

  public static void main(String[] args) {
        Set<String> a = new HashSet<>();
        for (int i = 97; i < 101; i++) {
            char c = (char) i;
            a.add(String.valueOf(c));
        }
        System.out.println(a); // [a, b, c, d]
        Set<String> b = new HashSet<>();
        for (int i = 99; i < 103; i++) {
            char c = (char) i;
            b.add(String.valueOf(c));
        }
        System.out.println(b); // [c, d, e, f]
        System.out.println(Sets.union(a,b)); // [a, b, c, d, e, f]
        System.out.println(Sets.intersection(a,b)); // [c, d]
        System.out.println(Sets.difference(a,b)); // [a, b]
        System.out.println(Sets.complement(a,b)); // [a, b, e, f]

    }

比较父子容器类间方法和接口的区别

class ContainerCom{
    // 类方法集合
    private static  Set<String> methods(Class c){
        Set<String> set = new TreeSet<>();
        for (Method method : c.getMethods()) {
            set.add(method.getName());
        }
        return set;
    }
    // 排除Object方法的类方法集合
    public static Set<String> methodWithOutObject(Class c){
        return Sets.difference(methods(c), methods(Object.class));
    }
    // 类接口集合
    public static Set<String> interfaces(Class c){
        Set<String> set = new TreeSet<>();
        for (Class anInterface : c.getInterfaces()) {
            set.add(anInterface.getSimpleName());
        }
        return set;
    }
	// 父类和子类方法/接口的区别
    public static void diff(Class superClass,Class subClass){
        System.out.println("方法区别:");
        System.out.println(Sets.difference(methodWithOutObject(superClass),methodWithOutObject(subClass)));
        System.out.println("接口区别:");
        System.out.println(Sets.difference(interfaces(superClass),interfaces(subClass)));
    }
    
}

 public static void main(String[] args) {
        ContainerCom.diff(List.class,ArrayList.class);
       diff(Collection.class,List.class);
    }

15.5 匿名内部类

class Person{
    private Person(){}
    public static Factory<Person> factory = new Factory<Person>() {
        @Override
        public Person next() {
            return new Person();
        }
    };
    
    public static Factory<Person> getFactory(){
        return new Factory<Person>() {
            @Override
            public Person next() {
                return new Person();
            }
        };
    }
}
 public static void main(String[] args) {
        Person p = Person.factory.next();
        p = Person.getFactory().next();
    }

15.6 构建复杂模型

class Person<A,B,C>{}

class PersonList<A,B,C> extends ArrayList<Person<A,B,C>>{}

  public static void main(String[] args) {
        List<Person<String,Integer,Integer>> list = new PersonList<>();
        list.add(new Person<>());
        Person<String, Integer, Integer> person = list.get(0);
    }

零售店模型

商店-走廊-货架-商品

// 商品
class Product{
    private Integer id;
    private Integer price;
    private static Random random = new Random();

    public Product(Integer id, Integer price) {
        this.id = id;
        this.price = price;
    }

    public static Factory<Product> getFactory(){
        return new Factory<Product>() {
            @Override
            public Product next() {
                int id = random.nextInt(10);
                int price = random.nextInt(100);
                return new Product(id,price);
            }
        };
    }

    @Override
    public String toString() {
        return new StringBuilder().append("商品编号:").append(id)
                .append("商品价格:").append(price).toString();
    }
}

// 货架
class Shelf extends ArrayList<Product>{
    // 一条货架要包含多少商品
    public Shelf(int productNum){
        for (int j = 0; j < productNum; j++) {
            add(Product.getFactory().next());
        }
    }
}

// 走廊
class Aisle extends ArrayList<Shelf>{
    // 一条走廊要包含多上货架,每条货架上是多少商品
    public Aisle(int shelfNum,int productNum){
        for (int i = 0; i < shelfNum; i++) {
            add(new Shelf(productNum));
        }
    }
}

// 商店
class Store extends ArrayList<Aisle> {
    public Store(int aisleNum,int shelfNum,int productNum){
        for (int i = 0; i < aisleNum; i++) {
            add(new Aisle(shelfNum,productNum));
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Aisle aisle : this) {
            for (Shelf shelf : aisle) {
                for (Product product : shelf) {
                    sb.append(product).append("\r\n");
                }
            }
        }
        return sb.toString();
    }
}
    public static void main(String[] args) {
        Store store = new Store(1,2,3);
        System.out.println(store);
    }
/*
商品编号:8商品价格:17
商品编号:1商品价格:39
商品编号:4商品价格:48
商品编号:0商品价格:91
商品编号:1商品价格:5
商品编号:2商品价格:37
*/

15.7 擦除的神秘之处

class A<T>{}

class B<T,U>{}
  public static void main(String[] args) {
        List<Integer> integerList = new ArrayList<>();
        List<String> stringList = new ArrayList<>();
      // 在运行时类型会被擦除,他们都是List
        System.out.println(integerList.getClass() == stringList.getClass()); // true
        A<Integer> a = new A<>();
        B<A<Integer>,String> b= new B<>();
      // getTypeParameters()返回泛型参数,但是并不是具体的类型参数,并没有实际意义
        System.out.println(Arrays.toString(a.getClass().getTypeParameters())); // [T]
        System.out.println(Arrays.toString(b.getClass().getTypeParameters())); // [T, U]
    }
15.7.1 C++的方式

翻译为Java的版本:

class HasF{
    public void f(){
        System.out.println("this is F");
    }
}

class Manipulator<T extends HasF>{
    private T obj;

    public Manipulator(T obj) {
        this.obj = obj;
    }

    public void manipulate(){
        // 类型擦除会擦除到它的上一个边界(可能会有多个边界)
        /*
        可以看到,这样使用泛型和使用HasF没有什么区别,
        如果是在类内部使用,就没必要使用泛型,
        在外部类中会使用该类型,才有必要使用泛型
         */
        obj.f();
    }
}
15.7.2 迁移兼容性

为了保持兼容性,泛型在运行时会被擦除。

15.7.3 擦除的问题

擦除也存在明显的代价,泛型不能用于显示地运行时类型操作,比如转型/instanceof/new,因为所有关于参数的类型信息都丢失了。

使用泛型并不是强制的。

public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.setT(1);
        Object o = a.getT();
        A<String> stringA = new A<>();
        stringA.setT("a");
        String t = stringA.getT();
    }
}

class A<T>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
15.7.4 边界的动作
class arrayMaker<T>{
    private Class<T> kind;

    public arrayMaker(Class<T> kind) {
        this.kind = kind;
    }

    @SuppressWarnings("unchecked")
    public T[] create(int size){
        // newInstance()方法返回Object对象,需要转型为T[],因为T是泛型,不存在类型信息,所以会产生警告信息
        return (T[]) Array.newInstance(kind,size);
    }
}

public class Test {
    public static void main(String[] args) {
        arrayMaker<String> arrayMaker = new arrayMaker<>(String.class);
        String[] strings = arrayMaker.create(5);
        System.out.println(Arrays.toString(strings));

    }
}
class ListMaker<T>{
    List<T> create(){
        return new ArrayList<>();
    }

    // 这里会产生警告
    @SuppressWarnings("unchecked")
    List<T> create2(){
        return new ArrayList();
    }
}

边界: 对象进入和离开方法的地点。

泛型的动作发生在边界处,对传递进来的值机型额外检查,对传递出去的值转型。

15.8 擦除的补偿

擦除丢失了泛型的类型信息,为了使用类型信息,需要显示的传递类型信息。

class A{}

class B extends A{}

class ClassType<T>{
    private Class<T> kind;
    // 显示传递类型信息
    public ClassType(Class<T> kind) {
        this.kind = kind;
    }

    public boolean is(Object obj){
        return kind.isInstance(obj);
    }
}

    public static void main(String[] args) {
        ClassType<B> classType = new ClassType<>(B.class);
        System.out.println(classType.is(new A())); // false
        System.out.println(classType.is(new B())); // true
    }
15.8.1 创建类型实例

内建的工厂对象

class ClassAsFactory<T>{
    public T t;

    public ClassAsFactory(Class<T> kind) {
        try {
            t = kind.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) {
        ClassAsFactory<String> classAsFactory = new ClassAsFactory<>(String.class);
        // 遇到无默认构造器的类会报错
       // ClassAsFactory<Integer> integerClassAsFactory = new ClassAsFactory<>(Integer.class);
    }

显式的工厂对象

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

// 具体对象
class A{
    public static class AFactory implements Factory<A>{
        @Override
        public A create() {
            return new A();
        }
    }
}

// 可接纳传入的对象
class Need<T>{
    public T t;
    // 注意:两个泛型必须对上,F代表的是工厂对象,而该工厂对象创建的对象是T
    public <F extends Factory<T>>Need(F f){
        t = f.create();
    }
}

    public static void main(String[] args) {
        Need<A> need = new Need<>(new A.AFactory());
    }

模板方法设计模式

abstract class Factory<T>{

    public T create(){
        return real();
    }

    protected abstract T real();
}

class AFactory extends Factory<String>{

    @Override
    protected String real() {
        return "a";
    }
}

  public static void main(String[] args) {
        Factory<String> factory = new AFactory();
        String s = factory.create();
    }
15.8.2 泛型数组
public class Test {
    static A<Integer>[] as;
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        // 并不能以这样的形势创建
        // as =  new A<Integer>[5];
        // 存在警告
        // 该数组在运行时依旧是Object数组
        // 创建泛型数组的方式就是创建一个被擦除类型的新数组,然后对其转型
        as = (A<Integer>[]) new A[5];
        as[0] = new A<>();
    }
}

class A<T>{}

泛型数组包装器

class FactoryArray<T>{
    private T[] ts;

    @SuppressWarnings("unchecked")
    public FactoryArray(int size){
        ts = (T[]) new Object[size];
    }

    public T get(int index){
        return ts[index];
    }

    public void set(int index,T t){
        ts[index] = t;
    }

    public T[] rep(){
        return ts;
    }
}

 public static void main(String[] args) {
        FactoryArray<Integer> factoryArray = new FactoryArray<>(5);
        factoryArray.set(0,1);
        Integer integer = factoryArray.get(0);
        /*
        数组将跟踪它们的实际类型,而这个类型是数组被创建时确定的,
        即时已被转型为Integer[],但这只存在于编译期,
        实际上它还是Object[]类型
         */
        // Integer[] integers = factoryArray.rep();
        Object[] objects = factoryArray.rep();
    }

另一种实现方式:内部使用Object[]储存

class FactoryArray<T>{
    private Object[] objects;

    @SuppressWarnings("unchecked")
    public FactoryArray(int size){
        objects = new Object[size];
    }

    public T get(int index){
        return (T) objects[index];
    }

    public void set(int index,T t){
        objects[index] = t;
    }

    public T[] rep(){
        return (T[]) objects;
    }
}

public static void main(String[] args) {
    FactoryArray<Integer> factoryArray = new FactoryArray<>(5);
    factoryArray.set(0,1);
    Integer integer = factoryArray.get(0);
    // 无论何种方式都不能推翻底层的数组类型
    // 该种方式的好处是使我们认识到无论何时底层都是Object[]类型
    // Integer[] rep = factoryArray.rep();
    Object[] rep = factoryArray.rep();
}

获得确切类型的数组

class FactoryArray<T>{
    private T[] ts;

    @SuppressWarnings("unchecked")
    // 通过传入类型,来弥补擦除的类型
    public FactoryArray(Class<T> tClass,int size){
        ts = (T[]) Array.newInstance(tClass,size);
    }

    public T get(int index){
        return ts[index];
    }

    public void set(int index,T t){
        ts[index] = t;
    }

    public T[] rep(){
        return ts;
    }
}

  public static void main(String[] args) {
        FactoryArray<Integer> factoryArray = new FactoryArray<>(Integer.class,5);
        // 这里会获得确切类型的数组
        Integer[] rep = factoryArray.rep();
    }

15.9 边界

边界可以用于泛型的参数类型上设置限制条件。

interface A{}

class B implements A{}

interface C extends A{}

class D{}

interface E{}

// 限定泛型的边界为A
class X<T extends A>{}

// T可以为多个的任意一个子类或实现类,第一个可以是类或接口,其他必须是接口
class Z<T extends D & A & E>{}

// T可以为任意A接口的继承接口或实现类的子实现类或子类,因为X已经限定了边界为A;
// 如果希望子类的泛型允许是多个的子类,则基类的泛型必须是接口,否则不符合上边的原理
class Y<T extends B & C> extends X<T>{}

15.10 通配符

数组向上转型存在的问题

class Fruit{}

class Apple extends Fruit{}

class BingApple extends Apple{}

class Orange extends Fruit{}

   public static void main(String[] args) {
        Fruit[] fruits = new Apple[5];
        fruits[0] = new Apple();
        fruits[1] = new BingApple();
        fruits[2] = new Orange(); // 抛出异常:ArrayStoreException
    }
/*
在编译期该数组向上转型是允许的,但是数组储存的就是其本来的类型,因此,在向Apple[]数组存入Orange[]数组时会出现异常
*/

尝试解决数组问题的一种方案

 public static void main(String[] args) {
        /*
        该种写法是不允许的,实际上,这不是向上转型,
        尽管Apple是Fruit的子类型,但是持有Apple类型的List
        不是持有Fruit类型的List的子类型。(可以这样理解,Fruit里包含
        的不仅仅是Apple类型,当存入Orange类型时,ArrayList就不能储存该
        类型;自动转型转的是List,不能把List中存的对象也转型了)
         */
        // List<Fruit> list = new ArrayList<Apple>();
    }

解决向上转型问题

  public static void main(String[] args) {
        /*
        该种形式是允许向上转型的,<? extends Fruit>代表的意思是
        这是Fruit的子类型,但是我不知道是哪个子类型。
        虽然向上转型成功了,但是并不能向里面放入Apple对象,因为
        其并不知道是哪个子类型。
        放入Fruit对象也不可以,因为里面存入的是Fruit的子类型,存
        Fruit类型相当于向下转型,因此是不可以的。
        但是,我们可以get一个Fruit对象,因为容器中的对象肯定是
        Fruit类型
         */
        List<? extends Fruit> list = new ArrayList<Apple>();
        // list.add(new Apple());
        // list.add(new Fruit());
    }
15.10.1 编译器有多聪明
   public static void main(String[] args) {
        List<? extends Fruit> list = new ArrayList<Apple>();
        Apple apple = (Apple) list.get(0);
        int index = list.indexOf(new Apple());
        boolean flag = list.contains(new Apple());
    }
/*
可以看到indexOf()等方法是可以接收各种类型的,为什么呢,因为他们的参数类型不是泛型,而是Object
*/

模拟上述案例

class Holder<T>{
    private T t;
    public Holder(T t){
        this.t = t;
    }
    public T get(){
        return t;
    }
    public void set(T t){
        this.t = t;
    }
    public boolean equals(Object obj){
        return t.equals(obj);
    }
}

public static void main(String[] args) {
        Holder<Apple> holder = new Holder<>(new Apple());
        Apple apple = holder.get();
        holder.set(new Apple());
        holder.equals(new Object());

        Holder<? extends Fruit> holder1 = holder;
        Fruit fruit = holder1.get();
        // holder1.set(new Apple()); // 不允许
        holder1.equals(new Object());
    }
15.10.2 逆变

超类型通配符<? super MyClass><? super T>

   public static void main(String[] args) {
        List<? super Apple> list = new ArrayList<Apple>();
        list.add(new Apple());
        list.add(new BingApple());
    }

总结:

<? extends MyClass>表达的含义是继承基类的某个子类,强调的含义是子类;<? super MyClass>表达的含义是基类的子类,强调的含义是基类;前者并不知道是哪个子类,因此,并不能向容器中添加对象,而后者知道基类,基类就一个,肯定就是这个基类,因此可以添加基类及子类的对象。

class Holder{
    static  <T> void a(List<T> list,T t){
        list.add(t);
    }

    static  <T>void b(List<? super T> list,T t){
        list.add(t);
    }

    public static void f1(){
        a(new ArrayList<Apple>(),new Apple());
        a(new ArrayList<Fruit>(),new Apple());
    }

    public static void f2(){
        b(new ArrayList<Apple>(),new Apple());
        b(new ArrayList<Fruit>(),new Apple());
    }
}
/*
书上说在f1()方法中,第二行代码不可以,在实验中是可以的
*/
15.10.3 无界通配符

<?>表示使用泛型编码,而不是使用原生类型。

class A{
    List list1;
    List<?> list2;
    List<? extends Object> list3;
    void f1(List list){
        list1 = list;
        list2 = list;
        list3 = list; // 会有警告
    }

    void f2(List<?> list){
        list1 = list;
        list2 = list;
        list3 = list;
    }

    void f3(List<? extends Object> list){
        list1 = list;
        list2 = list;
        list3 = list;
    }
}
/*
总体看,并无区别
*/

多个泛型参数,需要一个参数确定,而其他不确定时

class A{
    Map map1;
    Map<String,?> map2;
    Map<?,?> map3;
    void f(Map map){
        map1 = map;
        // 有警告
        // 为什么?map的key可能是任意类型,而map2的key是String类型,因此,可能出错
        map2 = map;
        map3 = map;
    }
}

更加详细的使用

class WildCards{
    static void rawArgs(Holder holder,Object args){
        /*
        为什么会有警告?
        Holder是一个泛型类型,这里被表示为原生类型;只要使用了原生类型,都会放弃编译期检查,因此是不安全的
         */
        holder.set(args); // unchecked警告
        holder.set(new WildCards()); // unchecked警告
        Object o = holder.get();
    }

    static void unboundArgs(Holder<?> holder, Object args){
        /*
        和上面的例子做对比,大部分人认为,这里会报警告而不是错误。
        为什么会出现错误?因为Holder表示包含任意类型,而Holder<?>表示某种具体类型,
        但是我们不知道这种类型是什么。
         */
        // holder.set(args); // Error
        Object o = holder.get();
    }

    static <T> T exact1(Holder<T> holder,T t){
        holder.set(t);
        return holder.get();
    }

    static <T> T exact2(Holder<? extends T> holder,T t){
        // holder.set(t); // Error
        return holder.get();
    }

    static <T> void exact3(Holder<? super T> holder,T t){
        holder.set(t);
        // 该类的超类并不仅仅是一个
        // T t1 = holder.get(); // Error
        // 因此,只有Object类型才是安全的
        Object object = holder.get();
    }
    	// 对exact3的解释
       public static void main(String[] args) {
        List<? super Apple> list = new ArrayList<>();
        List<? super Fruit> list1 = new ArrayList<>();
        list = list1;
    }
}
15.10.4 捕获转换

<?>参数传递原生类型参数,其会捕获原生类型的具体类型,从而可以向需要传递具体类型的需求传递该类型。

public class Test {
    public static void main(String[] args) {
        Holder<Integer> integerHolder = new Holder<>(1);
        f2(integerHolder);
        Holder holder = new Holder("a");
        f2(holder);
        Holder<?> wildCardHolder = new Holder<>(1.0);
        f2(wildCardHolder);
    }

    static <T>void f1(Holder<T> holder){
        System.out.println(holder.get().getClass().getSimpleName());
    }

    static void f2(Holder<?> holder){
        f1(holder);
    }
}
/*
如上,f2()先接收原生类型,再向f1()传递包含具体类型的参数。
但是,实际上直接调用f1()也是同样的效果。
可能是作者写书是用的JDK5,而在JDK7中已经有了类型推断这一功能。
*/
总结

可以看到,很多通过符组合,表示的意思是,其代表某种意思,但是我们不知道这个意思是什么,其大部分用途是在方法的形参上,让它代表这个意思,然后通过实参,传入具体的类型。

15.11 问题

基类类型不能用作泛型参数

public static void main(String[] args) {
        // List<int> list = new ArrayList<>();
        // 使用包装类型代替
        List<Integer> list = new ArrayList<>();
    }
public class Test {
    public static void main(String[] args) {
        // 自动包装机制局限:在数组中无法使用
        // int[] fs = f(new int[2]);
        //  int[] fs = f(new Integer[2]);
        Integer[] fs = f(new Integer[3]);
    }
    static <T> T[] f(T[] ts){
        return ts;
    }
}

实现参数化接口

一个类不能实现同一个泛型接口的两种变体。

interface A<T>{}

class B implements A<String>{}

// 编译不能通过
// class C extends B implements A<Integer>{}

如下写法可以

interface A<T>{}

class B implements A{}

class C extends B implements A{}

转型和警告

class A<T>{
    int index = 0;
    Object[] objects;
    A(int size){
        objects = new Object[size];
    }
    void push(T t){
        objects[index++] = t;
    }


    T pop(){
        // 警告
        // 因为泛型会被擦除,所以编译器无法知道转型是否安全
        return (T) objects[index--];
    }
}

有时泛型并不能消除转型需求

  public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream inputStream = new ObjectInputStream(null);
        // readObject()必须转型,但是它也不知道在读取什么
        // 泛型并没有消除转型需求
        List<String> list = (List<String>) inputStream.readObject();
        // 引入SE5的转型方式,但是仍然会产生警告(ps:那这有什么用。。。)
        list = (List<String>) List.class.cast(inputStream.readObject());
    }

重载

class A<W, T> {
    void f(List<W> list) {}
    void f(List<T> list){}
}
/*
由于泛型擦除,上述重载方法会产生相同的类型签名
*/

基类劫持了接口

class Pet implements Comparable<Pet>{
    @Override
    public int compareTo(Pet o) {
        return 0;
    }
}

// 一旦为接口确定了泛型参数,子类就不能做改变
// class SubPet extends Pet implements Comparable<SubPet>{}

class OtherPet extends Pet implements Comparable<Pet>{}

15.12 自限定的类型

自限定

class SelfBounded<T extends SelfBounded<T>>{
    private T t;
    public T get(){
        return t;
    }
    public void set(T t){
        this.t = t;
    }

    @Override
    public String toString() {
        return t.getClass().getSimpleName();
    }
}
class A extends SelfBounded<A>{}

class C extends SelfBounded<A>{}

	// 也可以用于方法
  static <T extends SelfBounded<T>> T f(T t){
        return t;
    }

    public static void main(String[] args) {
        A a = new A();
        a.set(a);
        A a1 = a.get();
        System.out.println(a);
        C c = new C();
        c.set(a1);
    }
/*
可以限定子类的泛型参数必须是SelfBounded的子类型
*/

疑问:

class SelfBound<T extends SelfBound>{
    private T t;
    public T get(){
        return t;
    }
    public void set(T t){
        this.t = t;
    }

    @Override
    public String toString() {
        return t.getClass().getSimpleName();
    }
}
/*
如上写法也是可以达到相同效果的,这两种有什么区别
*/

参数协变

自限定类型的价值在于它们可以产生协变参数类型——方法参数类型会随子类而变化。

返回类型:

class Base{}

class Drived extends Base{}

interface OrdinaryGatter{
    Base get();
}

interface DrivedGatter extends OrdinaryGatter{
    // 返回具体类的接口,需要重写接口
    @Override
    Drived get();
}
interface OrdinaryGatter<T extends OrdinaryGatter<T>>{
    T get();
}
// 使用自限定类型不需要重写接口
interface DrivedGatter extends OrdinaryGatter<DrivedGatter>{

}

 public static void main(String[] args) {
        DrivedGatter drivedGatter = null;
        drivedGatter.get();
    }

参数:

class OrdinarySatter{
    void set(Base base){}
}

class DrivedSatter extends OrdinarySatter{
    // 无法编译
    @Override // 注释后,会重载,产生两个set方法
    void set(Drived base) {}
}
// 使用自限定后可以让子类参数产生变化
interface OrdinarySatter<T extends OrdinarySatter<T>>{
    void set(T t);
}

interface DrivedSatter extends OrdinarySatter<DrivedSatter>{
}

  static void f(DrivedSatter satter){
        satter.set(satter);
    }

15.13 动态类型安全

由于以前代码不存在泛型机制,Java工具类Collections中提供了一系列的工具。

public class Test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(new Dog());
        fCat(list);
        List<Dog> dogList = Collections.checkedList(new ArrayList<>(), Dog.class);
        dogList.add(new Dog());
        fCat(dogList); // ClassCastException
        List<Pet> petList = Collections.checkedList(new ArrayList<>(), Pet.class);
        petList.add(new Dog());
        petList.add(new Cat());
        fCat(petList);
    }

    static void fCat(List list){
        list.add(new Cat());
    }
}

15.14 异常

抛出泛型异常

class Failure extends Exception{}

interface Process<E extends Exception>{
    void f() throws E;
}

class RunProcess implements Process<Failure>{

    @Override
    public void f() throws Failure {
        throw new Failure();
    }
    
      <F extends Exception> void e(){
        // 并不允许catch泛型异常
        /*
        try {
        }catch (F f){
        }
        */
    }
}

15.15 混型

混型 最基本的概念是混合多个类型的能力,以产生一个表示混型中所有类的能力。

混型的价值之一就是可以将特性和行为一致地应用于多个类之上;如果在混型类中修改某些东西,这些修改会应用于混型所应用的所有类型之上。

15.15.2 与接口混合

// 时间戳接口
interface TimeStamped{
    long getStamp();
}

class TimeStampedImpl implements TimeStamped{
    @Override
    public long getStamp() {
        return new Date().getTime();
    }
}

// 序列号
interface Number{
    int getNumber();
}

class NUmberImpl implements Number{
    private static int i = 0;
    @Override
    public int getNumber(){
        return ++i;
    }
}

class Basic{
    private String val;

    public String getVal() {
        return val;
    }

    public void setVal(String val) {
        this.val = val;
    }
}
// 混合类
// 实现相应的接口
class Mixin extends Basic implements TimeStamped,Number{
    // 每个混入类型都需要有相应的域
    private TimeStamped timeStamped = new TimeStampedImpl();
    private Number number = new NUmberImpl();
    @Override
    public long getStamp() {
        // 将功能交由具体类实现
        return timeStamped.getStamp();
    }

    @Override
    public int getNumber() {
        return number.getNumber();
    }
}

 public static void main(String[] args) {
        Mixin mixin = new Mixin();
        mixin.setVal("一混型");
        System.out.println(new StringBuilder(mixin.getVal())
                .append(" ")
                .append(mixin.getNumber())
                .append(" ")
                .append(mixin.getStamp())); // 一混型 1 1604302754656
    }
15.15.3 使用装饰器模式

装饰器模式使用分层对象来动态透明地向单个对象中添加责任,装饰器类和被装饰者有相同的接口。装饰器经常用于满足各种可能的组合,但是直接子类化,会产生过多的类。

// 装饰器类
class Decorator extends Basic{
    private Basic basic;
    public Decorator(Basic basic) {
        this.basic = basic;
    }

    @Override
    public String getVal() {
        return super.getVal();
    }

    @Override
    public void setVal(String val) {
        super.setVal(val);
    }
}

class NUmberImpl extends Decorator{
    private static int i = 0;

    public NUmberImpl(Basic basic) {
        super(basic);
    }
    // 可以添加自己的方法,但是具有局限性
    public int getNumber(){
        return ++i;
    }
}

class TimeStampedImpl extends Decorator{
    public TimeStampedImpl(Basic basic) {
        super(basic);
    }
    public long getStamp() {
        return new Date().getTime();
    }
}

 public static void main(String[] args) {
        NUmberImpl nUmber = new NUmberImpl(new Basic());
        TimeStampedImpl timeStamped = new TimeStampedImpl(nUmber);
    }
15.15.4 与动态代理混合
class MixinProxy implements InvocationHandler{
    private Map<String,Object> map;
    // 要代理的对象
    // 由于动态代理的限制,每个要混入的类必须是某个接口的实现
    public MixinProxy(/*代理对象和对应Class*/Map<Object,Class> maps) throws IllegalAccessException, InstantiationException {
        map = new HashMap<>();
       for(Map.Entry<Object,Class> entry:maps.entrySet()){
           for (Method method : entry.getValue().getMethods()) {
               // 将方法名与对象绑定
               map.put(method.getName(),entry.getKey());
           }
       }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        return method.invoke(map.get(name),args);
    }

    // 创建实例
    public static Object newInstance(Map<Object,Class> maps) throws InstantiationException, IllegalAccessException {
        MixinProxy mixinProxy = new MixinProxy(maps);
        List<Class> list = new ArrayList<>();
        for(Map.Entry<Object,Class> entry:maps.entrySet()){
            list.add(entry.getValue());
        }
        Class[] interfaces = new Class[list.size()];
        // List转数组
        list.toArray(interfaces);
        return Proxy.newProxyInstance(list.get(0).getClassLoader(),interfaces ,mixinProxy);
    }
}

 public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Map<Object,Class> map = new HashMap<>();
        map.put(new TimeStampedImpl(),TimeStamped.class);
        map.put(new NUmberImpl(),Number.class);

       Object o = MixinProxy.newInstance(map);
     
       // 缺点是必须强转为指定的类型
       TimeStamped timeStamped = (TimeStamped) o;
        System.out.println(timeStamped.getStamp()); // 1604306931322
        Number number = (Number) o;
        System.out.println(number.getNumber()); // 1
    }

15.16 潜在类型机制

编程要具有的思想:编写尽可能广泛地应用的代码,为了实现这一点,我们需要各种途径放松代码对类型所做的限制,同时不丢失静态类型检查的好处。

但是Java中当要在泛型上执行操作时,擦除会要求擦除到泛型的边界,这就对泛型的使用做了限制。

某些其他语言提供了一种潜在类型机制,只需要实现某个方法的子集,而不是特定的类或接口。

潜在类型机制是一种代码组织和复用机制,有了它编写的代码更加容易复用。

python示例:

class Dog:
    def speak(self):
        print "Arf"
    def sit(self):
        print "Sitting"

class Robot:
    def speak(self):
        print "Click"
    def sit(self):
        print "Clank"

def perform(anything):
    anything.speak()
    anything.sit()

a = Dog()
b = Robot()
perform(a)
perform(b)
# 可以看到Dog和Robot并没有什么关系,除了他们有两个同名方法。

15.17 对缺乏潜在类型机制的补偿

尽管Java不支持泛型机制,但仍然可以创建真正的泛型代码

反射

class Mime{
    public void sit(){
        System.out.println("我坐下");
    }

    @Override
    public String toString() {
        return "我";
    }
}

class Dog{
    public void sit(){
        System.out.println("狗坐下");
    }
    public void speak(){
        System.out.println("狗叫");
    }
}

public class Test2 {

    public static void perform(Object o){
        Class<?> aClass = o.getClass();
        try {
            Method sit = aClass.getMethod("sit");
            sit.invoke(o);
        } catch (Exception e) {
            // 同时可以处理异常
            System.out.println(o+"不能坐");
        }
        try {
            Method speak = aClass.getMethod("speak");
            speak.invoke(o);
        } catch (Exception e) {
            System.out.println(o+"不能说");
        }
    }

    public static void main(String[] args) {
        perform(new Mime());
        perform(new Dog());
    }
}

将一个方法应用于序列

实现编译器检查和潜在类型机制

将任意方法应用于对象的集合。

class Shape{
    public void dis(){
        System.out.println("未固定形状");
    }
}

class Round extends Shape{
    @Override
    public void dis() {
        System.out.println("圆形");
    }
}

public class Test2 {

    public static void main(String[] args) throws NoSuchMethodException {
        List<Shape> shapeList = new ArrayList<>();
       shapeList.add(new Shape());
       shapeList.add(new Round());
        Random random = new Random();
        apply(shapeList,shapeList.get(0).getClass().getMethod("dis"));
    }
	// T代表集合中的对象,S代表集合
    // 泛型中的意思:S必须继承Iterable(代表其是集合),后者的泛型代表,集合中存入的元素
    // 是T及其子类
    public static <T,S extends Iterable<? extends T>>void apply(S list,Method f,Object...args){
        try {
            for (T o:list){
                f.invoke(o,args);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

当你并未碰巧拥有正确的接口时

public class Test2 {
    public static void main(String[] args) throws NoSuchMethodException {
        apply(new ArrayList<>(),Shape.class,3);
    }
	
    // 该方法并不能接收MyList,尽管其也有add()方法
    // 这证明了潜在类型的价值,Java没有该机制,所以会受到特定类型的限制l
    public static <T>void apply(List<T> list,Class<? extends T> c,int num){
        try {
            for (int i = 0; i < num; i++) {
                list.add(c.newInstance());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

class MyList<T>{
    public void add(T t){}
}

用适配器仿真潜在类型机制

// 接口
interface Addable<T>{
    void add(T t);
}

class Fill2{
    // 仅接受Addable
    public static <T> void fill(Addable<T> addable,Class<T> c,int num){
        try {
            for (int i = 0; i < num; i++) {
                addable.add(c.newInstance());
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

// 适配器:将集合适配Addable
class AddableCollectionAdapter<T> implements Addable<T>{
    private Collection<T> collection;

    public AddableCollectionAdapter(Collection<T> collection) {
        this.collection = collection;
    }

    @Override
    public void add(T t) {
        collection.add(t);
    }
}

   public static void main(String[] args) throws NoSuchMethodException {
        Fill2.fill(new AddableCollectionAdapter<>(new ArrayList<>()),Shape.class,2);
    }

15.18 将函数对象用作策略

主要介绍了下策略模式的应用;

// 操作两个数
interface Combiner<T> {
    T combine(T x, T y);
}

// 一元函数
interface UnaryFunction<R,T>{
    R function(T t);
}

// 在一元函数基础上增加最终结果的接口
interface Collector<T> extends UnaryFunction<T,T>{
    T result();
}

// 一元谓词
interface UnaryPredicate<T>{
    boolean test(T t);
}

class IntegerAdder implements Combiner<Integer>{

    @Override
    public Integer combine(Integer x, Integer y) {
        return x+y;
    }
}

class IntegerSub implements Combiner<Integer>{
    @Override
    public Integer combine(Integer x, Integer y) {
        return x - y;
    }
}

class BigIntegerAdder implements Combiner<BigInteger>{
    @Override
    public BigInteger combine(BigInteger x, BigInteger y) {
        return x.add(y);
    }
}

class BigDecimalUlp implements UnaryFunction<BigDecimal,BigDecimal>{
    @Override
    public BigDecimal function(BigDecimal bigDecimal) {
        // 返回一个数字和距其最近的数字之间的距离
        return bigDecimal.ulp();
    }
}

// 实现比较
class GreaterThan</*实现比较接口*/T extends Comparable<T>> implements UnaryPredicate<T>{
    private T bound;

    public GreaterThan(T bound) {
        this.bound = bound;
    }

    @Override
    public boolean test(T x) {
        return x.compareTo(bound)>0;
    }
}

class MultIntegerCollector implements Collector<Integer>{
    private Integer val = 1;
    @Override
    public Integer function(Integer integer) {
        val *= integer;
        return val;
    }

    @Override
    public Integer result() {
        return val;
    }
}

// 函数处理类
class Functional{

    public static void main(String[] args) throws NoSuchMethodException, ScriptException {
        // 执行函数,获得结果
        List<Integer> integerList = Arrays.asList(1,2,3,4,5);
        Integer i = reduce(integerList,new IntegerAdder());
        System.out.println(i); // 累加 15
        i = reduce(integerList,new IntegerSub());
        System.out.println(i); // 累减 -13
        List<BigInteger> bigIntegerList = Arrays.asList(
                new BigInteger("1"),new BigInteger("2"),
                new BigInteger("3"));
        BigInteger bigInteger = reduce(bigIntegerList, new BigIntegerAdder());
        System.out.println(bigInteger); // 大整数累加 6

        // 获得函数处理后的结果
        Collector<Integer> collector = forEach(integerList, new MultIntegerCollector());
        System.out.println(collector.result()); // 120

        // 将集合执行后的结果存入List中返回
        List<BigDecimal> bigDecimalList =  Arrays.asList(
                new BigDecimal("1"),new BigDecimal("2"),
                new BigDecimal("3"));
        List<BigDecimal> transform = transform(bigDecimalList, new BigDecimalUlp());
        System.out.println(transform); // [1, 1, 1]

        // 返回符合结果的集合
        List<BigInteger> filter = filter(bigIntegerList, new GreaterThan<>(new BigInteger("2")));
        System.out.println(filter); // [3]
    }

    // 累计处理获得结果
    public static <T> T reduce(/*可迭代的集合*/Iterable<T> seq,Combiner<T> combiner){
        // 获得迭代器
        Iterator<T> iterator = seq.iterator();
        // 为什么不直接使用while?combine()方法需要两个参数,
        if (iterator.hasNext()){
            T result = iterator.next();
            while (iterator.hasNext()){
                result = combiner.combine(result,iterator.next());
            }
            return result;
        }
        return null;
    }

    // 执行一元函数
    // 通过接口定义参数,这里用到了策略模式,可以传入不同具体的实现
    public static <T> Collector<T> forEach(Iterable<T> seq,Collector<T> func){
        for (T t : seq) {
            func.function(t);
        }
        return func;
    }

    // 将集合执行后的结果存入List中返回
    public static <R,T> List<R> transform(Iterable<T> seq,UnaryFunction<R,T> func){
        List<R> result = new ArrayList<>();
        for (T t : seq) {
            result.add(func.function(t));
        }
        return result;
    }

    // 将集合中符合规则的元素存入List
    public static <T> List<T> filter(Iterable<T> seq,UnaryPredicate<T> pred){
        List<T> result = new ArrayList<>();
        for (T t : seq) {
            if (pred.test(t)){
                result.add(t);
            }
        }
        return result;
    }
}

15.19 总结

  • 泛型最大的价值体现在容器类的使用中;
  • 在容器中获得Object对象并转型,出现错误并无法发现并不常见;
  • 将容器对象合理命名可以很好的避免这种情况;
  • 如此显著和复杂的特性(泛型)并非仅仅为了解决转型的错误;
  • 泛型更重要的目的在于表达性;
  • 正如泛型的名字,目的是创建出更通用的代码。
  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值