java泛型

泛型

什么是泛型

泛型是为了不再进行强制类型转化,而提供的一个可供传入类型的模板。T<>

理解
public class ArrayList {
    private Object[] array;
    private int size;
    public void add(Object e) {...}
    public void remove(int index) {...}
    public Object get(int index) {...}
}

用Object表示就得进行强转
为了不进行强转需要
public class StringArrayList {
    private String[] array;
    private int size;
    public void add(String e) {...}
    public void remove(int index) {...}
    public String get(int index) {...}
}

public class IntegerArrayList {
    private Integer[] array;
    private int size;
    public void add(Integer e) {...}
    public void remove(int index) {...}
    public Integer get(int index) {...}
}
。。。。会有很多
于是
public class ArrayList<T> {
    private T[] array;
    private int size;
    public void add(T e) {...}
    public void remove(int index) {...}
    public T get(int index) {...}
}
T可以是任何class。这样一来,我们就实现了:编写一次模版,可以创建任意类型的ArrayList:

泛型接口

可以在接口中定义泛型类型,实现此接口的类必须实现正确的泛型类型
除了ArrayList<T>使用了泛型,还可以在接口中使用泛型。
java内置的一个比较大小的接口
public interface Comparable<T> {
    /**
     * 返回-1: 当前实例比参数o小
     * 返回0: 当前实例与参数o相等
     * 返回1: 当前实例比参数o大
     */
    int compareTo(T o);
}


public class Main {
    public static void main(String[] args) {
        Person[] ps = new Person[] {
            new Person("Bob", 61),
            new Person("Alice", 88),
            new Person("Lily", 75),
        };
        Arrays.sort(ps);
        System.out.println(Arrays.toString(ps));
    }
}
class Person implements Comparable<Person> {
    String name;
    int score;
    Person(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public int compareTo(Person other) {
        return this.name.compareTo(other.name);
    }
    public String toString() {
        return this.name + "," + this.score;
    }
}

泛型类

public class Pair<T, K> {
    private T first;
    private K last;
    public Pair(T first, K last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public K getLast() { ... }
}
相当于Map<K,V>

注意点:静态方法不能引用泛型类型<T>,必须定义其他类型(例如<K>)来实现静态泛型方法;
public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public T getLast() { ... }

    // 静态泛型方法应该使用其他类型区分:
    public static <K> Pair<K> create(K first, K last) {
        return new Pair<K>(first, last);
    }
}

擦拭法

泛型是一种类似”模板代码“的技术,不同语言的泛型实现方式不一定相同。
虚拟机对泛型其实一无所知,所有的工作都是编译器做的

还是进行的强转 不过强转是根据传入的类型编译器进行强转

由于java泛型的原理是擦拭法,会有以下几个局限性
1. <T>不能是基本类型
2. 无法取得带泛型的Class  这个是因为类型是我们传给编译器,编译器进行强转的,所以只有一个T在jvm里只是Object,当然没有获取具体的
换句话说,所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,
因为编译后它们全部都是Pair<Object>。
3. 无法判断带泛型的类型(和2是一个意思)
Pair<Integer> p = new Pair<>(123, 456);
// Compile error:
if (p instanceof Pair<String>) {
} 
4. 不能实例化T类型
public class Pair<T> {
    private T first;
    private T last;
    public Pair() {
        // Compile error:
        first = new T();
        last = new T();  这个就是new Object 没有具体的类型了,所以编译器肯定不认可
    }
}
可以这么写
public class Pair<T> {
    private T first;
    private T last;
    public Pair(Class<T> clazz) {
        first = clazz.newInstance();
        last = clazz.newInstance();
    }
}

不恰当的覆写方法

由于擦释法 会吧T看成Object所以 方法里不不起equals这样名字 会出现覆写的问题

泛型继承

一个类可以继承自一个泛型类
class IntPair extends Pair<Integer> {
    public IntPair(Integer first, Integer last) {
        super(first, last);
    }
}

extends通配符

1. 作为方法参数如何使用
public class Main {
    public static void main(String[] args) {
        Pair<Integer> p = new Pair<>(123, 456);
        int n = add(p);
        System.out.println(n);
    }
    
    static int add(Pair<Number> p) { // 这个地方接受的是Number 但是传入的是Integer 泛型没有类型的继承 所以会报错
        //怎么解决呢  应该用泛型的extends
        //static int add(Pair<? extends Number> p) {  //这样写就可以传入Integer了
        // 这个函数用了extends 肯定再不能写setFirst() 因为这个时候的T无法确定了啊 可能是Number或者子类,编译器不再能确定
        // 注意 这个相当于属性是只读的了,不能set了 这是另一个extends的作用,set null是会通过的
        
        Number first = p.getFirst();  // 这个地方类型必须还是Number 因为擦拭法
        Number last = p.getLast();
        return first.intValue() + last.intValue();
    }
}
使用extends通配符表示可以读,不能写

2. 使用extends限定T类型,作为类的继承
public class Pair<T extends Number> { ... }
使用类似<T extends Number>定义泛型类时表示:
泛型类型限定为Number以及Number的子类

super通配符

考察Pair<? super Integer>的setFirst()方法,它的方法签名实际上是:
void setFirst(? super Integer);
因此,可以安全地传入Integer类型。
public static void main(String[] args) {
    Pair<Number> p1 = new Pair<>(12.3, 4.56);
    Pair<Integer> p2 = new Pair<>(123, 456);
    setSame(p1, 100);
    setSame(p2, 200);
    System.out.println(p1.getFirst() + ", " + p1.getLast());
    System.out.println(p2.getFirst() + ", " + p2.getLast());
}

static void setSame(Pair<? super Integer> p, Integer n) {
    p.setFirst(n);
    p.setLast(n);
}
但是 这个时候getFirst就不知道用什么接受了 , Object类型可以
表示方法内部代码对于参数只能写,不能读

对比extends和super通配符

<? extends T>允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
<? super T>允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。
这个拷贝list的例子很安全的体现了这个特性
public class Collections {
    // 把src的每个元素复制到dest中:
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        for (int i=0; i<src.size(); i++) {
            T t = src.get(i);
            dest.add(t);
        }
    }
}
Collections.copy 这个java源码就是这么写的

PECS原则

记住这个特性的口诀 Producer Extends Consumer Super

无限定通配符

无限定通配符<?>很少使用,可以用<T>替换,同时它是所有<T>类型的超类
只写了<?> 既没有extends 也没有super
void sample(Pair<?> p) {
}
换句话说,既不能读,也不能写,那只能做一些null判断:
大多数情况下,可以引入泛型参数<T>消除<?>通配符
所以:<?>通配符有一个独特的特点,就是:Pair<?>是所有Pair<T>的超类:
public class Main {
    public static void main(String[] args) {
        Pair<Integer> p = new Pair<>(123, 456);
        Pair<?> p2 = p; // 安全地向上转型
        System.out.println(p2.getFirst() + ", " + p2.getLast());
    }

}

class Pair<T> {
    private T first;
    private T last;

    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }

    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setLast(T last) {
        this.last = last;
    }
}

泛型和反射

Class<String> s = String.class;
Class<? super String> sup = String.class.getSuperclass();

泛型的数组问题
Pair<String>[] ps = null; // ok
Pair<String>[] ps = new Pair<String>[2]; // compile error!
必须通过强制转型实现带泛型的数组:
@SuppressWarnings("unchecked")
Pair<String>[] ps = (Pair<String>[]) new Pair[2];可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型;

可以通过Array.newInstance(Class<T>, int)创建T[]数组,需要强制转型;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰明子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值