Java泛型

Posted yangfei629

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java泛型相关的知识,希望对你有一定的参考价值。

1、什么是泛型


 

泛型就是 参数类型化 ,简单说就是在定义类 接口 方法时时不支持具体的参数类型的。只有在实际使用时才确定。

 

2、为何用泛型


 

我觉得有两点好处

a.  明确类型 避免类型转换

 

    如,没有泛型前,从List中取数据都是Object类型的 需要强制转换为确定的类型

public static void main(String[] args) 
        List myList = new ArrayList();
        myList.add(new Integer(1));
        Integer value = (Integer)(myList.get(0));
        System.out.println("value=" + value);
    

 

b.  提高代码复用

  如有Father类 每个父亲可能有不同的职业 有点的教师 有点的医生... 

  如何表示呢,搞多个类表示不同的职业? 可以 但不友好!

  使用泛型 如下:把职业类型化 

  使用:  

Father<Teacher> fatherTeacher = new Father<>(new Teacher());
Father<Doctor> fatherDoctor = new Father<>(new Doctor());
public class Father<T> 
    private T job;
    
    public Father(T job)
        this.job = job;
    
    
    public void work()
        System.out.println("my job is "+ job);
    

  

3、如何使用泛型

 


 

泛型可以使用在类(泛型类)、 接口(泛型接口、 方法(泛型方法)中。

泛型类: 如上述的例子

 

泛型接口

样例

技术图片
public interface Work<T> 
    public void work(T t);


继承泛型接口
public class Father<T> implements Work<T>
    private T job;

    public Father(T job)
        this.job = job;
    

    @Override
    public void work(T t) 
        System.out.println("my job is "+ t);
    

View Code

 

泛型方法:方法的参数调用时才确定

例如下 :在泛型类中定义了一个泛型方法 (E是一种类型,跟类中的T 可能一样可能不一样)

泛型方法必须使用<> 表示类型

public class Father<T> implements Work<T>
    private T job;

    public Father(T job)
        this.job = job;
    

    @Override
    public void work(T t) 
        System.out.println("my job is "+ t);
    

    public <E> void printWork(E e)
        System.out.println("my job is "+ e);
    

  调用:

技术图片
public static void main(String[] args) 
        Father<Teacher> fatherTeacher = new Father<>(new Teacher());
        fatherTeacher.printWork(new Teacher());
    
View Code

输出:my job is com.yangfei.test.fanxing.Teacher@13221655

 

4、泛型擦除


 

泛型只在编译时有效 运行时是没有泛型的,称之为泛型擦除。

如下的示例 输出结果为true (java中一个类只会加载一次 生成Class类型对象)

说明两个类是没有泛型的区别的(编译后都被转成了Object)。

Class list_int = new ArrayList<Integer>().getClass();
Class list_string = new ArrayList<String>().getClass();
System.out.println(list_int==list_string);

  

泛型擦除的目的: 就是为了运行时不会创建过多的类 避免因泛型的区别导致加载的类泛滥。

 

 

泛型擦除非真正擦除: 否则运行时如何转换成实际的类型呢

List<String> sd = new ArrayList<String>();

sd.add("yangfei");

String str = list.get(0) 

但取值的时候怎么知道要转成String类型的呢?

擦除并非真正的完全擦除,泛型信息还是会保存在class字节码的常量池中,

在使用了泛型的代码调用处会生成一个signature签名字段,signature指明了这个常量在常量池的地址,调用时强制转换

这样我们就找到了参数化类型。

参考: https://www.jianshu.com/p/2ded3ffaa9c8

 

5、协变


 

数组是协变的 什么意思

如果class A extends B 那么B[] array = new A[] 也是成立的,这是协变。

泛型是不支持协变的

即 List<A>是不可以赋给List<B>的

 

6、 ?和T的区别


 

T表示一个类型 在使用时确定。

?时通配符 表示任意类型。

 

使用场景不一样

a. 在泛型类定义是使用T

b.在使用泛型时 可用? 

如: 

Father<?> fatherTeacher = new Father<Teacher>(new Teacher());

 

有啥好处,我们知道泛型不支持协变的,有了通配符后,可以有如下表示

父子类:class Son extends Mather; 则如下写法也是合法的

List<? extends Mather> lists = new ArrayList<Son>();

其实List<?> 相当于List<? extends Object>

 

7、PECS原则


 

PECS原则的全拼 : "Producer Extends Consumer Super"

表示<? extends T> 一般用于生产者,生产数据。外部只从中读数据;< ? super T> 一般用于消费者,需要消费数据,外部需要往其中写数据。

 

7.1 <? extends T> 表示可能是T或者T的子类 (上边界通配符)

  这种情况一般只能取 不能存非null之外的任何类型

如下class Son extends Mather; class Daughter extends Mather;

public static void main(String[] args) 
        Mather[] matherArr = new Mather[]new Daughter(),new Son();
        List<? extends Mather> lists = Arrays.asList(matherArr);
        lists.add(null); //因为任何类型都可以用null表示
        lists.add(new Daughter()); //此处会编译报错
        Mather mather = lists.get(0);
    

存非null值会报错 ,因为不知道具体是什么类型,Mather的子类和子类间并没有直接关系 没法赋值,所以没法存

取出来的值 只能是父类型(所有子类都可以转为父类)

 

7.2 <? super T> 表示可能是T或者T的父类 (下边界通配符)

  这种情况一般只用于存数据 ,可以存T或T的子类,因为T的子类可以转换为T类型,而T类型也可以自动向上转为任何父类

  取出的数据向上转换只能是object类型 

public static void main(String[] args) 
        Mather[] matherArr = new Mather[]new Daughter(),new Son();
        List<? super Mather> lists = Arrays.asList(matherArr);
        lists.add(null);
        lists.add(new Daughter());
        Object mather = (Object)lists.get(0);
    

  

8、不支持泛型数组

 


 

   Java是不支持泛型数组的,如下编译会报错

List<String>[] array = new ArrayList<String>[5];

 为何不支持:数组支持协变,泛型运行时类型擦除,这样容易引起误解

如下: List<Integer>类型数组 因为类型参数,可以把List<String>也放入数组中,导致内容错误。

ArrayList<Integer>[] intArr = new ArrayList<Integer>[10];
Object[] obj = intArr;
         
ArrayList<String> listStr = new ArrayList<String>();
listStr.add("yangfei")
obj[0] = listStr;
        
ArrayList<Integer> listInt = intArr[0];
Integer i = listInt.get(0);//想要获取Integer,但却是String

参考: https://www.cnblogs.com/hapjin/p/5371950.html

 

9、静态方法与类的泛型


 

  一个泛型类只有在初始化时才能明确其类型 ; 而静态方法不属于实例。所以静态方法不能使用类的泛型

如下:标红会编译报错

public class Father<T> implements Work<T>
    private T job;

    public Father(T job)
        this.job = job;
    

    @Override
    public void work(T t) 
        System.out.println("my job is "+ t);
    

    public static  void printWork(T e)
        System.out.println("my job is "+ e);
    
    

解决方法:可使用泛型方法   

 

以上是关于Java泛型的主要内容,如果未能解决你的问题,请参考以下文章

Java泛型 VS C#泛型 (伪泛型 VS 真泛型)

java中啥叫泛型??

java中啥是泛型,怎么用泛型?

Java——泛型

java泛型的一些知识点:Java泛型--泛型应用--泛型接口泛型方法泛型数组泛型嵌套

Java 泛型泛型简介 ( 泛型类 | 泛型方法 | 静态方法的泛型 | 泛型类与泛型方法完整示例 )