<T>泛型,广泛的类型

其实早在1999年的JSR 14规范中就提到了泛型概念,知道jdk5泛型的使用才正式发布,在jdk7后,又对泛型做了优化,泛型的推断.

泛型类

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

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

    public void setFirst(T newValue) {
        first = newValue;
    }

    public void setSecond(T newValue) {
        second = newValue;
    }
}

怎么理解泛型类的定义.首先在类名后根上<T>这个T就是任意类型.在 Java 库中, 使用变量 E 表示集合的元素类型, K 和 V 分别表示表的关键字与值的类型。T ( 需要时还可以用临近的字母 U 和 S) 表示“ 任意类型”。然后在类中的成员,都可以使用这个T,你既可以把T当做参数,也可以把T当做返回值.也可以把T当做成员变量的类型.这个T到底存储的什么类型,取决于你在实例化Pair时指定的具体类型.但是以上写法,你一旦指定了一个实际类型,那么这个类中所有的T都会是同一个类型.

public class Main {
    
    public static void main(String[] args) {
        Pair<String> pair = new Pair<>();
        pair.setFirst("第一");
        pair.setSecond("第二");
    }
}

你也可以在一个泛型类上定义多个泛型

public class Pair<T,U> {
    private T first;
    private U second;

    public Pair() {
        first = null;
        second = null;
    }

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

    public void setFirst(T newValue) {
        first = newValue;
    }

    public void setSecond(U newValue) {
        second = newValue;
    }
}

但是你需要记得,因为泛型的作用域在类级别.一下写法是错误的.

 

 

 你在类上定义了个T表示,你所实例化的每一个类都要指定一个类型,现在,现在你试图不做类的实例化,而直接使用T,那么这个T你要从哪里定义呢?记住要使用泛型,先确定泛型的具体类型.

泛型方法

public class Demo3 {
    public   <T> void show(T t){
        System.out.println(t.toString());
    }
    public static  <S> void show2(S s){
        System.out.println(s);
    }
public class Main {
    public static void main(String[] args) {
        Demo3 demo3 = new Demo3();
        demo3.<String>show("a");
        demo3.show("a");
        Demo3.show2(1);
    }
}

你在一个方法上指定了泛型,即泛型的作用域在方法体上,也就是说,你每次调用方法都要指定具体的类型.当然你不用每次调用都使用<T>语法,因为jdk7的泛型推断.编译器自然可以通过你的实参而推断出你想要的实际类型.将泛型定义到方法上,泛型的T可以用到参数,方法体,返回值.

当然在你指定泛型时,也可以有以下写法

public class Demo1 {
    public  <String> void add(String t){
    }
}

不过这通常是没有任何意义的,否则,你要想表达什么呢?定义了一个泛型方法,并且限定泛型的实际类型是String?

泛型的擦除

泛型的擦除可谓是泛型中的重中之重了.字面意思,泛型类型会被擦除.引起两个问题:1在什么情况下擦除2被擦除后的的类什么样.

文档说明泛型只在编译器用来检测类型,编译时即会擦除泛型.所以泛型是在编译时被擦除的.下面看一下代码

public class Demo4<T> {
    private T type;

    public void add(T t) {
    }
}

 

 

 可以看出在无限定类型时(没有使用extends 或 super 限定泛型)在编译后原来的T被替换成了Object.

 

泛型表达式

看以下代码

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

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

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}
public class Main {
    public static void main(String[] args) {
        Pair<Student> pair = new Pair<>();
        Student s = pair.getFirst();
    }
}

前边已经说过,对于无限定类型,在编译时会擦掉泛型的而变成object.那么以上这个Main中运行的代码,pair通过get()方法的返回值确可以直接赋值给Student这又是怎么回事呢?

 

 

 

 

 

 这是两个class反编译后的.可以看到,在get()方法后,编译器帮我们自动做了类型转换

泛型与多态的冲突(桥方法)

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

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

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}

 

public class PairChild extends Pair<Person> {
    @Override
    public void setFirst(Person first) {
        super.setFirst(first);
    }
}

我们继承了Pair并且指定了他的具体类型.那么我们覆盖Pair方法时就只能传入Person类型的参数了.那么,在已经编译好的Pair.class中,setFirst()应该还是Object类型.这时我们到底算是覆盖父类的方法了吗?

Pair.class

 

 

 PairChild.class

 

 

其中object参数的方法就是桥方法,当我们使用setFirst时,会先调用这个桥方法,这个桥方法,会将object强制转换成Person然后在调用PairChild自己的setFirst().

打破泛型的约束

有时间泛型对待数据类型也不是绝对安全的.请看一下示例

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

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

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}
public class PairSort  {
    public void sort(Pair pair){
        pair.setFirst("可是我是字符串");
    }
}
public class Main {
    public static void main(String[] args) {
        PairSort pairSort = new PairSort();
        Pair<Person> pair = new Pair();
        pairSort.sort(pair);
        Person first = pair.getFirst();
    }
}

我们有一个PairSort类,这个类接受一个Pair实例,但是并没有指定泛型,也就是说,它现在接受的是一个Object.然后给他的却是一个指定了Person类型的Pair.这时我们在调用Pair就获得了一个错误.

 

 约束与限制

1 不能使用基本类型实例化类型参数,我们不能传递基本数据类型当做泛型的具体类型,原因是当泛型擦除后需要转换成具体的object子类.而基本数据类型,并不能转换成object

2 检查一个对象的类型不能带泛型参数.看一下代码

 

 

 3 不能创建参数化类型的数组

 

 

 4 不能实例化类型变量

 

 5 尽管有泛型的擦除,但是在静态中依然不能使用泛型

 

 6 泛型类中不能覆盖父类的方法

通配符

请看以下错误代码

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

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

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}
public class Employee {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Manager extends Employee {
}
public class Main {

    public static void main(String[] args) {
        Pair<Manager> pair = new Pair<>();
        printBuddies(pair);//错误的
    }

    public static void printBuddies(Pair<Employee> p) {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName());
    }
}
printBuddies()需要一个Pair类型参数,我们指定Pair的泛型类型是Employee.当我们传递实参时,传递的是Pair<Manager>类型的实参,这是不正确的.Pair<Employee>和Pair<Manager>没有父子关系.
他们都只是Pair类型.那么对于这种问题有没有解决方案的?
public class Main {

    public static void main(String[] args) {
        Pair<Manager> pair = new Pair<>();
        printBuddies(pair);
        
        Pair<Employee> employeePair = new Pair<>();
        printBuddies(employeePair);
    }

    public static void printBuddies(Pair<? extends Employee> p) {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName());
    }
}

使用 ? extends Employee 指定Pair的泛型类型是Employee或者是其子类.这样就不会出现编译异常

看一下对通配符的使用

public class Main {
    public static void main(String[] args) {
        Pair<Manager> pair  = new Pair<>();
        Pair<? extends Employee> pair1 = pair;
        pair.setFirst(new Employee());//错误的
        pair.setFirst(new Manager());
        Employee first = pair.getFirst();
    }
}

 

我们创建一个Manager类型的Pair.将他赋值给Pair<Employee>这是没错的.但是当我们调用set方法则会出现编译异常.原因是编译器知道我们要传入Employee的子类型但是不知道具体传入的是哪个子类型所以

拒绝编译.但是get方法则没有问题,因为返回是一个Employee

超类通配符

public class Main {

    public static void main(String[] args) {
        printBuddies(new Pair<Employee>());
        printBuddies(new Pair<Manager>());
    }

    public static void printBuddies(Pair<? super Manager> p) {
        Object first = p.getFirst();
        Object second = p.getSecond();
    }
}

我们限定printBuddies的参数为Manager或者其父类.

 

 

 由于我们希望传入Manager或者其父类所以get方法拒绝我们用一个Manager接收.它无法确定我们到底传入的是Manager还是其父类所以只能用Object接收.

 

无限定通配符

 

 

 

 

 

 虽是无限定通配符,但是他的使用限定是最大的.我们甚至无法使用set方法.除非传递一个null.Object都不行.在get方法时我们也只能用Object来接收.那么为什么要有这样一个鸡肋的通配符呢?

posted @ 2019-09-27 19:31  顶风少年  阅读(1541)  评论(0编辑  收藏  举报
返回顶部