java泛型

来源:互联网 发布:阿里云ecs绑定几个域名 编辑:程序博客网 时间:2024/06/05 09:04

一、泛型的最大价值

经常出现同一个算法适合几种数据类型,为了编写通用的算法,我们使用泛型。

也就是在保证类型安全的前提下,把算法和数据类型解耦。


二、泛型类

public class Person<T> {private T t;public Person(T t){this.t = t;}public String toString(){return "参数的类型是:" +                       t.getClass().getCanonicalName();}

子类怎么继承父类的T呢

public class Teacher<T,S> extends Person<T> {protected T t;private S s;public Teacher(T t) {super(t);}public void set(V v, S s){this.v = v;this.s = s;}


泛型类怎么调用

public class Generics {public static void main(String[] args){Person<Integer> p = new Person<Integer>(5);System.out.print(p.toString());}}


三、泛型接口
public interface Factory<T> {public T create();}

具体实现:

public class Car {}public class Computer {}public class CarFacotry implements Factory<Car> {@Overridepublic Car create() {System.out.println("装载发动机!");System.out.println("装载座椅!");System.out.println("装载轮子!");return new Car();}}public class ComputerFactory implements Factory<Computer> {@Overridepublic Computer create() {System.out.println("装载主板!");System.out.println("装载CPU!");System.out.println("装载内存");return new Computer();}}

调用:

public class GenericTest {public static void  main(String[] args) throws Exception{Factory<Car> carFactory = new CarFacotry();Factory<Computer> computerFactory = new ComputerFactory();System.out.println("======开始生产车子!=======");carFactory.create();System.out.println("=====开始生产电脑!========");computerFactory.create();}}

四、泛型方法

泛型方法是在方法上声明类型参数,该类型参数只可以作用于声明它的方法上

public class Factory {public <T> T generator (Class<T> t) throws Exception{return t.newInstance();}}

调用:

public class Teacher{public static void main(String[] args) throws Exception{Factory factory = new Factory();Date date = factory.generator(Date.class);System.out.print(date.toString());}}


不能理解的话,点击这里:java中的泛型方法


五、泛型边界

1)通配符类型,表示任何类型,符号是“?”

2)<? extends A> ,设定上行边界,限定传入的具体参数类型,只能是A的子类或A

3)<? super A>,设定下行边界,限定传入的具体参数类型,只能是A的父类或A

4)例子:

public class Animal {}public class Bird extends Animal {}public class Fish extends Animal {}public class Zoo<T> {private T t;public Zoo(T t){this.t = t;}public T pop(){return this.t;}}

调用:

public class GenericTest {public static void  main(String[] args) throws Exception{Zoo<? extends Animal> zoo = new Zoo<Bird>(new Bird());zoo = new Zoo<Fish>(new Fish());//zoo = new Zoo<Integer>(5); //不合法}Zoo<? super Bird> zoo1 = new Zoo<Bird>(new Bird());zoo1 = new Zoo<Animal>(new Animal());//zoo1 = new Zoo<Fish>(new Fish()); //不合法


5)多边界的泛型

<T extends Speakable&Flyable>,要求T是实现了Speakable和Flyable这两个接口的类


六、泛型擦除

1)泛型只存在语法层次,一旦编译后,就不存在

当泛型存在于编译时,一旦被确认安全使用时,就会将其转换为原生类型也就是擦除成原生类型


2)为什么要擦除

擦除后,泛型变成原生类型,使泛化的代码可以使用非泛化的类库


3)擦除的实质

将原有的类型参数替换成非泛化的上界

例子:

public class Computer<E> {    private E e;    public Cmputer(E e){    this.e=e;}    public E apply(){    return this.e;        }}

编译后:

public class Computer {    private Object e;    public Computer(Object e){    this.e=e;}    public Object apply(){    return e;            }}
因为Computer<E>没有指明上界,所以被擦除成Object类型


4)多边界擦除

<T extends Speakable&Flyable>,选择排在前面的边界进行参数替换,这里是Speakable。。换下位置就是Flyable


七、泛型的限制和问题

1)为了保证类型安全,不能使用泛型类型参数创建实例

   / /不合法          T object=new T();

因为运行时参数类型已经被擦除了,不知道T到底是什么类型,有没有无参构造方法,或者可能是个抽象类,不能实例化


2)

不能声明泛型实例数组,这会导致运行错误

//不合法         T[] number= new T[capcity];

但可以通过创建一个Object类型数组然后将它的类型转换E[]来规避这个限制。

E[] number= (E[])new Object[capcity];


3)在静态的环境下不允许参数类型是泛型类型的

4)泛型类对象无法被抛出或捕获,因为泛型类不能继承或实现Throwable接口及其子类

这句话不要理解错了,不要以为不可以try-catch和throws,他们都是可以的,只不过不能下面这样

try{

}catch(T t) // 正常情况下是catch(Exception e)


public class GenericException<T> extends Exception{}


原因是什么,上面说的很清楚了,看加粗字体


5)instanceof 不起作用

Zoo<Fish> birdZoo = new Zoo<Fish>();if(birdZoo instanceof Zoo<Bird>){...} //这个if一定会是true的,原因是擦除了,都是Zoo类型

那有没有能起作用的地方呢?有

Zoo<Bird> birdZoo = new Zoo<Bird>();if(birdZoo instanceof Zoo<?>){...} //这里的if就不会总是true了,因为instanceof判断允许使用参数类型为通配符的泛型

6)多态冲突

例子:

public class Animal<T> {public void set(T t){//泛型擦除后,就变成了 set(Object t)System.out.println("1111");}}public class Bird extends Animal<String>{public void set(String name){//按理来说,这个方法应该是重写,但是由于擦除的原因,父类中此方法的参数是Object,//而这里是String,所以不构成重写,对多态是个影响super.set(name);//但是这句话还是能调用成功的}}

调用:

public class Test {public static void main(String[] args) {Bird bird = new Bird();Animal<String> animal = bird;animal.set("bird");}}

怎么解决多态冲突呢?JVM会自动生成桥方法,不用程序员自己写

public void set(Object obj){//这个例子中,这个桥方法是生成在Bird类里面的。set((String)obj);}

再来个例子

public class Animal<T> {public T get(){System.out.println("Animal get");return null;}}public class Bird extends Animal<String>{public String get(){System.out.println("Bird get");return null;}}

调用:

public class Test {public static void main(String[] args) {Bird bird = new Bird();Animal<String> animal = bird;animal.get();}}
输出结果是:Bird get


这里就有个疑问了,Bird的桥方法 Object get()与本来就有的方法 String get(),只是返回值不同,java中不是不准这样的吗?

原因是这是在JVM中,而JVM确定一个方法还要根据返回值




                                                                                           

原创粉丝点击