美文网首页程序员
想理解泛型吗?看这一篇就够了!

想理解泛型吗?看这一篇就够了!

作者: 套马杆的程序员 | 来源:发表于2020-12-06 22:23 被阅读0次

一、前言
二、泛型类
2.1 概述
Java中泛型使用情况大致包括三种:泛型类、泛型接口、泛型方法

本节演示泛型类。

2.2 代码
2.2.1 泛型类一个字母、显式指定泛型实参
package mypackage泛型类;

//泛型类一个字母、显示指定泛型实参 则当前泛型由实参指定
//此时,注意传入的实参必须为引用类型,不能为基本类型,如果是8种基本类型,则传入它们的包装类型
public class Test {

public static void main(String[] args) {
    GenericClass genericClass = new GenericClass<Integer>(123);
    GenericClass genericClass2 = new GenericClass<String>("abc");
    System.out.println(genericClass);
    System.out.println(genericClass2);
}

}

class GenericClass<T> { // 定义泛型类 就是在定义类的时候,类名后面加上<T> 这个字母任意取 可以是T E V或者其他字母

private T key;

public T getKey() {
    return key;
}

public void setKey(T key) {
    this.key = key;
}

public GenericClass(T key) {
    super();
    this.key = key;
}

@Override
public String toString() {
    return "GenericClass [key=" + key + "]";
}

}
输出1:

GenericClass [key=123]
GenericClass [key=abc]
小结1:泛型类一个字母、显示指定泛型实参 则当前泛型由实参指定
注意:此时传入的实参必须为引用类型,不能为基本类型,如果是8种基本类型,则传入它们的包装类型

2.2.2 泛型类一个字母,不显示指定泛型实参
package mypackage泛型类2;

//泛型类一个字母、不显示指定泛型实参
public class Test {

public static void main(String[] args) {
    // 虽然不显式指定T 但是传入参数的时候动态指定了T
    GenericClass genericClass = new GenericClass(123); // T为Integer
    GenericClass genericClass2 = new GenericClass("abc"); // T为String
    System.out.println(genericClass);
    System.out.println(genericClass2);

}

}

class GenericClass<T> { // 定义泛型类 就是在定义类的时候,类名后面加上<T> 这个字母任意取 可以是T E V或者其他字母

private T key;

public T getKey() {
    return key;
}

public void setKey(T key) {
    this.key = key;
}

public GenericClass(T key) {
    super();
    this.key = key;
}

@Override
public String toString() {
    return "GenericClass [key=" + key + "]";
}

}
输出2:

GenericClass [key=123]
GenericClass [key=abc]
小结2:泛型类一个字母、不显示指定泛型实参,虽然不显式指定T ,但是传入参数的时候动态指定了T

2.2.3 泛型类两个字母、显式指定泛型实参
package mypackage泛型类;

//泛型类两个字母、显示指定泛型实参
public class Test1 {

public static void main(String[] args) {
    GenericClass1 genericClass = new GenericClass1<String, Integer>("小A", 95);

    System.out.println(genericClass);

}

}

class GenericClass1<K, V> { // 定义泛型类 就是在定义类的时候,类名后面加上<T> 这个字母任意取 可以是T E V或者其他字母

private K key;
private V value;

public K getKey() {
    return key;
}

public void setKey(K key) {
    this.key = key;
}

public V getValue() {
    return value;
}

public void setValue(V value) {
    this.value = value;
}

@Override
public String toString() {
    return "GenericClass [key=" + key + ", value=" + value + "]";
}

public GenericClass1(K key, V value) {
    super();
    this.key = key;
    this.value = value;
}

}
输出3:

GenericClass [key=小A, value=95]
小结3:泛型类两个字母、显示指定泛型实参,直接在一个字母扩展就好,同理多个字母都可以扩展

2.2.4 泛型类两个字母,不显示指定泛型实参
package mypackage泛型类2;

//泛型类两个字母、不显示指定泛型实参
public class Test1 {

public static void main(String[] args) {
    // 虽然不显式指定T 但是传入参数的时候动态指定了T
    GenericClass1 genericClass = new GenericClass1("小A", 123); // T为Integer
    System.out.println(genericClass);

}

}

class GenericClass1<K, V> { // 定义泛型类 就是在定义类的时候,类名后面加上<T> 这个字母任意取 可以是T E V或者其他字母

private K key;
private V value;

public K getKey() {
    return key;
}

public void setKey(K key) {
    this.key = key;
}

public V getValue() {
    return value;
}

public void setValue(V value) {
    this.value = value;
}

@Override
public String toString() {
    return "GenericClass1 [key=" + key + ", value=" + value + "]";
}

public GenericClass1(K key, V value) {
    super();
    this.key = key;
    this.value = value;
}

}
输出4:

GenericClass1 [key=小A, value=123]
小结4:泛型类两个字母、不显示指定泛型实参,直接在一个字母扩展就好,同理多个字母都可以扩展

2.3 小结
本节演示泛型类,关于泛型接口、泛型方法且看下一节。

三、泛型接口和泛型方法
3.1 概述
本节演示泛型接口和泛型方法。

3.2 代码
3.2.1 泛型接口一个字母
package mypackage泛型接口;

import java.util.Random;

//泛型接口和泛型类的区别   
//都是在定义的时候后面<T>  字母任意指定   区别就是具体类可以实例化对象,接口不能实例化对象,必须被类实现,然后才能拿到客户端运行
public class Test {
   public static void main(String[] args) {
       GenericInterface generic = new GenericClass();
       System.out.println(generic.getT());
   }
}
interface GenericInterface<T> { // 定义接口的时候后面有<T>,则为泛型接口 字母可以任意指定
   // 接口中所有方法默认都是public
   T getT();
}
// 因为接口不能实例化对象,这里新建一个类实现接口,给客户端用 所以类也是泛型类
class GenericClass implements GenericInterface<String> {
   private String[] provinces = new String[] { "湖南", "江西", "广东" };

   @Override
   public String getT() {
       return provinces[new Random().nextInt(3)];
   }

}

输出1:
广东
小结1:泛型接口不能实例化对象,所以定义GenericClass类实现GenericInterface接口,实现泛型接口实例化。

3.2.2 泛型接口两个字母
package mypackage泛型接口;

import java.util.Random;

//泛型接口和泛型类的区别   
//都是在定义的时候后面<T>  字母任意指定   区别就是具体类可以实例化对象,接口不能实例化对象,必须被类实现,然后才能拿到客户端运行
public class Test1 {

   public static void main(String[] args) {
       GenericInterface1 generic = new GenericClass1();
       System.out.println(generic.getK() + " - " + generic.getV());
   }

}

interface GenericInterface1<K, V> { // 定义接口的时候后面有<T>,则为泛型接口 字母可以任意指定
   // 接口中所有方法默认都是public
   K getK();

   V getV();
}

// 因为接口不能实例化对象,这里新建一个类实现接口,给客户端用 所以类也是泛型类
class GenericClass1 implements GenericInterface1<String, String> {
   private String[] names = new String[] { "小A", "小B" };
   private String[] provinces = new String[] { "湖南", "江西", "广东" };

   public String getK() {
       return names[new Random().nextInt(2)];
   }

   public String getV() {
       return provinces[new Random().nextInt(3)];
   }

}

输出2:
小A - 江西
小结2:泛型接口两个字母,在一个字母上扩展即可

3.2.3 泛型方法一个字母
package mypackage泛型方法;

//泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
public class Test {

   public static void main(String[] args) {
       GenericClass genericClass = new GenericClass<Integer>(123);
       GenericClass genericClass2 = new GenericClass<String>("abc");
       System.out.println(genericClass);
       System.out.println(genericClass2);
       
       
       System.out.println(genericClass._displayKey(genericClass));
       System.out.println(genericClass2._displayKey(genericClass2));
   }

}
class GenericClass<T> { // 定义泛型类 就是在定义类的时候,类名后面加上<T> 这个字母任意取 可以是T E V或者其他字母

   private T key;

   public T getKey() {
       return key;
   }

   public void setKey(T key) {
       this.key = key;
   }

   public GenericClass(T key) {
       super();
       this.key = key;
   }

   @Override
   public String toString() {
       return "GenericClass [key=" + key + "]";
   }
  /** 
    * 这是一个真正意义上的泛型方法。
    * 泛型方法标志:在访问控制符与返回值之间的<T>不可或缺,表明这是一个泛型方法,并且声明了一个泛型T
    * 这个T声明之后,这个T就可以出现在这个泛型方法的任意位置.
    */
   public <T>  T _displayKey(GenericClass<T> genericClass){
       System.out.println("===============泛型方法============");
       return genericClass.getKey();
   }
   //泛型方法的几种错误:
   //错误一,没有在返回值前尖括号<>中声明的字母不能用,否则编译报错
//  public <T> T _displayKey1(GenericClass<E> genericClass){  //E cannot be resolved to a type
//      return genericClass.getKey();
//  }
//  public <T> E _displayKey2(GenericClass<T> genericClass){  //E cannot be resolved to a type
//      return genericClass.getKey();
//  }
   //错误二:没有在返回值前尖括号这个标志的都不是泛型方法
   //这不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。   关键:没有在返回值前尖括号这个标志
   public void showKeyValue1(GenericClass<Number> obj){
     System.out.println("泛型测试 key value is " + obj.getKey());
   }

   //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?    关键:没有在返回值前尖括号这个标志
   public void showKeyValue2(GenericClass<?> obj){
     System.out.println("泛型测试 key value is " + obj.getKey());
   }
}

输出3:
GenericClass [key=123]
GenericClass [key=abc]
===============泛型方法============
123
===============泛型方法============
abc
小结3:泛型类,是在实例化类的时候指明泛型的具体类型;同理,泛型方法,是在调用方法的时候指明泛型的具体类型 。意义上是一样的,泛型字母都是先声明后使用,只是范围不同,泛型类、泛型接口定义时声明的字母对类或接口内容均有效,泛型方法定义时声明的字母对方法内容有效

金手指:
泛型方法: 在访问控制符和返回值之间
解释:因为在返回值之前只有关键字,不会使用到泛型T,但是从返回值开始,
就开始使用到泛型T了,毕竟返回值自己就使用泛型T
判断真假泛型方法的key:是否在返回值前有尖括号这个标志

3.2.4 泛型方法两个字母
package mypackage泛型方法;

public class Test1 {
   public static void main(String[] args) {
       GenericClass1 genericClass = new GenericClass1<String, Integer>("小A", 95);
       System.out.println(genericClass);
       System.out.println(genericClass._displayKey(genericClass));
   }
}

class GenericClass1<K, V> { // 定义泛型类 就是在定义类的时候,类名后面加上<T> 这个字母任意取 可以是T E V或者其他字母

   private K key;
   private V value;

   public K getKey() {
       return key;
   }

   public void setKey(K key) {
       this.key = key;
   }

   public V getValue() {
       return value;
   }

   public void setValue(V value) {
       this.value = value;
   }

   @Override
   public String toString() {
       return "GenericClass [key=" + key + ", value=" + value + "]";
   }

   public GenericClass1(K key, V value) {
       super();
       this.key = key;
       this.value = value;
   }

   /**
    * 这是一个真正意义上的泛型方法。 泛型方法标志:在访问控制符与返回值之间的<T>不可或缺,表明这是一个泛型方法,并且声明了一个泛型T
    * 这个T声明之后,这个T就可以出现在这个泛型方法的任意位置.
    */
   public <K, V> GenericClass1<K, V> _displayKey(GenericClass1<K, V> genericClass) {
       System.out.println("===============泛型方法============");
       return genericClass;
   }

}

输出4:

GenericClass [key=小A, value=95]
===============泛型方法============
GenericClass [key=小A, value=95]
小结4:泛型方法两个字母,在一个字母上扩展即可

3.3 小结
本节介绍泛型接口和泛型方法,演示了一个泛型字母、两个泛型字母,多个泛型字母可以依次类推。

四、泛型通配符与上下限、泛型擦除
4.1 概述
本节介绍泛型通配符与上下限、泛型两种擦除方式。且见代码1、代码2、代码3.

4.2 代码
4.2.1 泛型通配符与上下限
package mypackage泛型通配符和上下限;

import java.util.ArrayList;
import java.util.List;

public class Test {

   public static void main(String[] args) {
       List<Integer> _iIntegers_list = new ArrayList<>();
       List<String> _sStrings_list = new ArrayList<>();
       List<Number> _nNumbers_list = new ArrayList<>();
       List<Object> _oObjects_list = new ArrayList<>();

       function1(_iIntegers_list);// function1接收的实参是Number及其子类 Integer是Number子类
                                   // 这里正确
       // function1(_sStrings_list);//function1接收的实参是Number及其子类 String与Number无关
       // 这里错误
       function1(_nNumbers_list);// function1接收的实参是Number及其子类 这里Number正确
       // function1(_oObjects_list);//function1接收的实参是Number及其子类 Object是Number父类
       // 这里错误

       // function2(_iIntegers_list);//function2接收的实参是Number及其父类
       // Integer是Number子类 这里错误
       // function2(_sStrings_list);//function2接收的实参是Number及其父类 String与Number无关
       // 这里错误
       function2(_nNumbers_list);// function2接收的实参是Number及其父类 这里Number正确
       function2(_oObjects_list);// function2接收的实参是Number及其父类 Object是Number父类
                                   // 这里正确

       function3(_iIntegers_list); // function3接收的实参是?是一种类型实参,可以看做所有类的父类
       function3(_sStrings_list);// function3接收的实参是?是一种类型实参,可以看做所有类的父类
       function3(_nNumbers_list);// function3接收的实参是?是一种类型实参,可以看做所有类的父类
       function3(_oObjects_list);// function3接收的实参是?是一种类型实参,可以看做所有类的父类
   }

   // 这不是一个泛型方法,返回值前面没有尖括号<T>,泛型上限 此时泛型是?,?是Number及其子类 接收的实参是Number及其子类
   private static void function1(List<? extends Number> list) {
       System.out.println("function1");
   }

   // 这不是一个泛型方法,返回值前面没有尖括号<T>,泛型下限 此时泛型是?,?是Number及其父类 接收的实参是Number及其父类
   private static void function2(List<? super Number> list) {
       System.out.println("function2");
   }

   // 泛型上限extends的产生是因为泛型之间没有类似多态的思想,
   // 泛型为Number只能接收Number,不能接收Integer,即使Intger是Number子类,所有有了泛型上限
   // 泛型下限与泛型上限相对应,但使用super关键字
   // 演示通配符 ?是一种类型实参,可以看做所有类的父类
   private static void function3(List<?> list) {
       System.out.println("function3");
   }
}

输出1:

function1
function1
function2
function2
function3
function3
function3
function3

小结1:因为泛型没有类似多态的想法,所以产生了通配符?和上下限(extends super).

泛型上限extends的产生是因为泛型之间没有类似多态的思想
泛型为Number只能接收Number,不能接收Integer,
即使Integer是Number子类,所以有了泛型上限
泛型下限与泛型上限相对应,但使用super关键字
演示通配符 ?是一种类型实参,可以看做所有类的父类

4.2.2 泛型擦除(自动擦除)
package mypackage泛型擦除;

import java.util.ArrayList;
import java.util.List;

//泛型擦除包括自动擦除和手动擦除
//自动擦除是指泛型仅在编译时有效,运行时则是相同的类型                                       编译之后程序会自动去泛型,即称为自动擦除
//手动擦除:将有泛型的集合赋给不带泛型的集合,此时泛型被擦除(手动擦除).    
//注意:所有的泛型报错都是编译时报错,因为泛型只在编译时有效

//演示自动擦除
public class Test {

   public static void main(String[] args) {
       List<String> list = new ArrayList<>();
       // list.add(123); //报错,编译时报错,因为类型和泛型不同,说明泛型在编译时是有效的
       // 注意:所有泛型报错都是编译时报错,因为泛型只在编译时有效
       List<Integer> list2 = new ArrayList<>();
       System.out.println(list.getClass() + " - " + list2.getClass());
       if (list.getClass().equals(list2.getClass())) {
           System.out.println("类型相同"); // 打印这句,说明泛型在运行时无效了,因为编译后就被擦除了
       }
   }

}

输出2:
class java.util.ArrayList - class java.util.ArrayList
类型相同
小结2:泛型只在编译时有效,编译后自动擦除。

4.2.3 泛型擦除(手动擦除)
package mypackage泛型擦除;

import java.util.ArrayList;
import java.util.List;

//泛型擦除包括自动擦除和手动擦除
//自动擦除是指泛型仅在编译时有效,运行时则是相同的类型                                       编译之后程序会自动去泛型,即称为自动擦除
//手动擦除:将有泛型的集合赋给不带泛型的集合,此时泛型被擦除(手动擦除).    
//注意:所有的泛型报错都是编译时报错,因为泛型只在编译时有效

//演示泛型手动擦除
public class Test1 {

   public static void main(String[] args) {
       List<String> list = new ArrayList<String>();
       list.add("abc");

       List list2 = list; // 将有泛型的集合赋给不带泛型的集合,此时泛型被擦除(手动擦除).
       list2.add(123); // 这里编译时没有报错,完美证明泛型已经被擦除

       for (Object object : list2) {
           System.out.println(object);
       }

   }

}

输出3:
abc
123
小结3:将有泛型的集合赋给不带泛型的集合,此时泛型被擦除(手动擦除).

4.3 小结
本节介绍泛型通配符和上下限,泛型两种擦除方式。

五、泛型之堆污染
5.1 概述
Java堆污染的定义:Heap pollution(堆污染), 指的是当把一个不带泛型的对象赋值给一个带泛型的变量时, 就有可能发生堆污染.

Java堆污染的原因:因为在定义泛型对象(泛型类的对象)时,是否显示指定泛型类型不是强制的(可以在后面的运行中确定),这就造成了ClassCastException隐患,这个异常隐患不会在编译时抛出,而是在运行时抛出。

5.2 代码
5.2.1 未带泛型造成的堆污染
package mypackage堆污染;

import java.util.*;
//第一种,未指定泛型造成的堆污染
//将未显示指定泛型的对象,运行中确定范型后,赋值给与其泛型不同的对象,而造成的堆污染
public class Test {

   public static void main(String[] args) {
   List list=new ArrayList();
   list.add(123);
   List<String> list2=list;//这里将不带泛型的list集合再次赋值给带泛型String的list集合    产生了堆污染
   System.out.println(list2.get(0)); //堆污染出现

   }

}

输出1:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at mypackage堆污染.Test.main(Test.java:12)
小结1:堆污染产生只有一个原因,就是把一个不带泛型的对象赋值给一个带泛型的变量,代码1演示最原始的情况。

附:代码1抑制堆污染(编译时去掉警告)——加上@SuppressWarnings(“unchecked”)就好:

package mypackage堆污染;

import java.util.*;
//第一种,未指定泛型造成的堆污染
//将未显示指定泛型的对象,运行中确定范型后,赋值给与其泛型不同的对象,而造成的堆污染

public class Test {

   @SuppressWarnings({ "unchecked", "rawtypes" })
   public static void main(String[] args) {
   List list=new ArrayList();
   list.add(123);
   List<String> list2=list;//这里将不带泛型的list集合再次赋值给带泛型String的list集合    产生了堆污染
   System.out.println(list2.get(0)); //堆污染出现

   }

}

如果因为泛型擦除而造成不带泛型,进入被带泛型变量步骤,最后造成堆污染,且看代码2.

5.2.2 泛型擦除后泛型消失造成的堆污染
package mypackage堆污染;

import java.util.ArrayList;
import java.util.List;
//第二种,手动擦除后造成的堆污染
public class Test1 {

   public static void main(String[] args) {
       List<Integer> intList = new ArrayList<>();
       intList.add(1);

       List list = intList;  //带泛型的list集合赋值给无泛型的list集合,这是泛型的手动擦除
       List<String> lst = list;//这里将擦除后不带泛型的list集合再次赋值给带泛型String的list集合    产生了堆污染

       System.out.println(lst.get(0));   //这里体现了堆污染  ClassCastException
   }

}

输出2:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at mypackage堆污染.Test1.main(Test1.java:15)
小结2:堆污染产生只有一个原因,就是把一个不带泛型的对象赋值给一个带泛型的变量,代码2演示开始带泛型list,然后手动擦除泛型,最后被不带泛型的对象赋值,造成堆污染。

附:代码2抑制堆污染(编译时去掉警告)——加上@SuppressWarnings(“unchecked”)就好:

package mypackage堆污染;

import java.util.ArrayList;
import java.util.List;
//第二种,手动擦除后造成的堆污染
public class Test1 {

   @SuppressWarnings({ "unchecked", "rawtypes" })
   public static void main(String[] args) {
       List<Integer> intList = new ArrayList<>();
       intList.add(1);

       List list = intList;  //带泛型的list集合赋值给无泛型的list集合,这是泛型的手动擦除
       List<String> lst = list;//这里将擦除后不带泛型的list集合再次赋值给带泛型String的list集合    产生了堆污染

       System.out.println(lst.get(0));   //这里体现了堆污染  ClassCastException
   }

}

5.2.3 泛型数组作为函数参数造成的堆污染
package mypackage堆污染;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

//第三种,因为泛型数组而带来的堆污染
//java 语言中不允许创建泛型数组, 所以当可变参数为带泛型的可变数组时, 方法内只能用不带泛型的数组接收.     将带泛型的数组赋值给不带泛型数组,造成堆污染
public class Test2 {

   public static void main(String[] args) {
       List<String> list = new ArrayList<>();
       list.add("abc");
       _function(list);

   }

   public static void _function(List<String>... sLists) { //
       List[] _listArray = sLists; // //java 语言中不允许创建泛型数组, 方法内只能用不带泛型的数组接收.
       // 但是参数sLists又是带泛型的集合,这里将带泛型的集合赋值给不带泛型的数组,有代码2中擦除的味道,为后面造成隐患
       List<Integer> _tempList = Arrays.asList(1);

       _listArray[0] = _tempList;// 这里是关键,_tempList有泛型Integer,_listArray无泛型,
                                   // 将带泛型的list集合赋值给不带泛型list数组,造成堆污染

       String string = sLists[0].get(0); // 每次都是这里,取出来的时候报错

   }
}

输出3:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at mypackage堆污染.Test2._function(Test2.java:26)
at mypackage堆污染.Test2.main(Test2.java:14)
小结3:堆污染产生只有一个原因,就是把一个不带泛型的对象赋值给一个带泛型的变量,代码3演示因为泛型数组造成的堆污染,因为java语言中不允许创建泛型数组, 方法内只能用不带泛型的数组接收带泛型的数组实参,有代码2中泛型擦除的味道,后面又将带Integer泛型的_templist赋值给不带泛型的_listarray,正式造成堆污染,最后get(0)并赋值的时候这个堆污染的错误显示出来。

附:代码3抑制堆污染(编译时去掉警告)——加上@SuppressWarnings(“unchecked”)就好:

package mypackage堆污染;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

//第三种,因为泛型数组而带来的堆污染
//java 语言中不允许创建泛型数组, 所以当可变参数为带泛型的可变数组时, 方法内只能用不带泛型的数组接收.     将带泛型的数组赋值给不带泛型数组,造成堆污染
public class Test2 {

   @SuppressWarnings("unchecked")
   public static void main(String[] args) {
       List<String> list = new ArrayList<>();
       list.add("abc");
       _function(list);

   }

   @SuppressWarnings({ "unused", "rawtypes", "unchecked" })
   public static void _function(List<String>... sLists) { //
       List[] _listArray = sLists; // //java 语言中不允许创建泛型数组, 方法内只能用不带泛型的数组接收.
       // 但是参数sLists又是带泛型的集合,这里将带泛型的集合赋值给不带泛型的数组,有代码2中擦除的味道,为后面造成隐患
       List<Integer> _tempList = Arrays.asList(1);

       _listArray[0] = _tempList;// 这里是关键,_tempList有泛型Integer,_listArray无泛型,
                                   // 将带泛型的list集合赋值给不带泛型list数组,造成堆污染

       String string = sLists[0].get(0); // 每次都是这里,取出来的时候报错

   }
}

5.3 小结
本节演示了泛型堆污染,三种方式产生的泛型堆污染,本质上就是一个意思“把一个不带泛型的对象赋值给一个带泛型的变量时, 就有可能发生堆污染.”

六、泛型和多态区别
Java语言中,泛型和多态是两个完全不同东西,没有任何交集。

Java中泛型是伴随集合框架和产生的,是从Java5开始支持的新的语法,为实现广泛通用的类型.(集合框架或自定义的泛型类、泛型接口、泛型方法)

多态即向上转型,所谓的上和下的产生是由类与类、类与接口之间的继承、实现联系在一起的,继承实现是多态的基础。Java实现运行时绑定,只有在运行时才可以知道一个引用真正指向的对象类型,编译时是不知道的。

综上,泛型和多态的区别:

1、泛型总是和集合框架联系在一次(当然,程序员也可以自定义泛型类、泛型方法、泛型接口);

多态总是和类与类、类与接口、接口与接口的继承实现联系在一起,是一种向上转型。

2、泛型是编译时有效,编译后自动擦除;多态一般是运行时绑定,运行时有效。

3、任意时刻只能指定一种泛型且泛型没有父子概念,泛型为Number是不可以接收子类Integer类型变量;

任意时候只能指定一种对象类型但是有父子概念,多态保证父类形参可以接收子类实参。

七、尾声
泛型,就是这么简单,完成了。

天天打码,天天进步!!!

相关文章

  • 想理解泛型吗?看这一篇就够了!

    一、前言二、泛型类2.1 概述Java中泛型使用情况大致包括三种:泛型类、泛型接口、泛型方法 本节演示泛型类。 2...

  • Mac 端 Flutter 的环境配置看这一篇就够了

    Mac 端 Flutter 的环境配置看这一篇就够了Mac 端 Flutter 的环境配置看这一篇就够了

  • 【泛型】通配符与嵌套

    上一篇 【泛型】泛型的作用与定义 1 泛型分类 泛型可以分成泛型类、泛型方法和泛型接口 1.1 泛型类 一个泛型类...

  • pymongo中的特殊查询

    python操作mongo常用的增删改查看这一篇就够了Python操作MongoDB看这一篇就够了 最近在整理一些...

  • 2、泛型方法

    从来没写过泛型方法,这次项目中用到了。泛型方法理解:(1)对比泛型类,List,我的理解是:泛型为了防止不必...

  • 单点登录(SSO)

    参考:单点登录(SSO)看这一篇就够了

  • 泛型(二)之泛型基础

    从上一篇泛型(一)之为何引入泛型可以知道了没有泛型的世界是多么的悲催,引入泛型带来的便利,下面就详细正式的介绍一下...

  • Java泛型

    本文介绍的知识点 泛型是什么? 泛型的使用在反射中使用泛型在集合类中使用泛型 关于泛型擦除如何理解?如何避免泛型擦...

  • 一文带你看懂java 泛型,史上最全面的泛型教学啦

    认真看这篇文章,保证你们对泛型又有新的理解,如果没有的话,请顺着网线来打我呀。 概述 引用下百度百科的回答 泛型是...

  • java lambda表达式

    关于Java Lambda表达式看这一篇就够了

网友评论

    本文标题:想理解泛型吗?看这一篇就够了!

    本文链接:https://www.haomeiwen.com/subject/sbmawktx.html