编程语言
首页 > 编程语言> > java 泛型全解 - 绝对最详细

java 泛型全解 - 绝对最详细

作者:互联网

1、泛型的概述:

1.1 泛型的由来

根据《Java编程思想》中的描述,泛型出现的动机:

有很多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类。
复制代码

泛型的思想很早就存在,如C++中的模板(Templates)。模板的精神:参数化类型

1.2 基本概述

1.3 泛型的目的

1.4 实例

JDK 1.5时增加了泛型,在很大的程度上方便在集合上的使用。

public static void main(String[] args) {
        List list = new ArrayList();
        list.add(11);
        list.add("ssss");
        for (int i = 0; i < list.size(); i++) {
            System.out.println((String)list.get(i));
        }
    }
复制代码

因为list类型是Object。所以int,String类型的数据都是可以放入的,也是都可以取出的。但是上述的代码,运行的时候就会抛出类型转化异常,这个相信大家都能明白。

public static void main(String[] args) {
        List<String> list = new ArrayList();
        list.add("hahah");
        list.add("ssss");
        for (int i = 0; i < list.size(); i++) {
            System.out.println((String)list.get(i));
        }
    }
复制代码

在上述的实例中,我们只能添加String类型的数据,否则编译器会报错。


2、泛型的使用

泛型的三种使用方式:泛型类泛型方法泛型接口

2.1 泛型类

public class 类名 <泛型类型1,...> {
    
}
复制代码

2.2 泛型方法

public <泛型类型> 返回类型 方法名(泛型类型 变量名) {
    
}
复制代码
class Demo{  
  public <T> T fun(T t){   // 可以接收任意类型的数据  
   return t ;     // 直接把参数返回  
  }  
};  
public class GenericsDemo26{  
  public static void main(String args[]){  
    Demo d = new Demo() ; // 实例化Demo对象  
    String str = d.fun("汤姆") ; // 传递字符串  
    int i = d.fun(30) ;  // 传递数字,自动装箱  
    System.out.println(str) ; // 输出内容  
    System.out.println(i) ;  // 输出内容  
  }  
};
复制代码

2.3 泛型接口

public interface 接口名<泛型类型> {
    
}
复制代码
/**
 * 泛型接口的定义格式:        修饰符  interface 接口名<数据类型> {}
 */
public interface Inter<T> {
    public abstract void show(T t) ;
}

/**
 * 子类是泛型类
 */
public class InterImpl<E> implements Inter<E> {
    @Override
    public void show(E t) {
        System.out.println(t);
    }
}


Inter<String> inter = new InterImpl<String>() ;
inter.show("hello") ;
复制代码

2.4 源码中泛型的使用,下面是List接口和ArrayList类的代码片段。

//定义接口时指定了一个类型形参,该形参名为E
public interface List<E> extends Collection<E> {
   //在该接口里,E可以作为类型使用
   public E get(int index) {}
   public void add(E e) {} 
}

//定义类时指定了一个类型形参,该形参名为E
public class ArrayList<E> extends AbstractList<E> implements List<E> {
   //在该类里,E可以作为类型使用
   public void set(E e) {
   .......................
   }
}
复制代码

2.5 泛型类派生子类

父类派生子类的时候不能在包含类型形参,需要传入具体的类型

public class A extends Container<K, V> {}

public class A extends Container<Integer, String> {}

public class A extends Container {}

2.6 泛型构造器

public class Person {
    public <T> Person(T t) {
        System.out.println(t);
    }
    
}
复制代码

使用:

public static void main(String[] args) {
    new Person(22);// 隐式
    new <String> Person("hello");//显示
}
复制代码

2.7 高级通配符

2.7.1背景:

2.7.2 <? extends T> 上界通配符

2.7.3 <? super T> 下界通配符

2.7.4 <?> 无界通配符


3、泛型擦除

3.1 概念

编译器编译带类型说明的集合时会去掉类型信息

3.2 验证实例:

public class GenericTest {
    public static void main(String[] args) {
        new GenericTest().testType();
    }

    public void testType(){
        ArrayList<Integer> collection1 = new ArrayList<Integer>();
        ArrayList<String> collection2= new ArrayList<String>();
        
        System.out.println(collection1.getClass()==collection2.getClass());
        //两者class类型一样,即字节码一致
        
        System.out.println(collection2.getClass().getName());
        //class均为java.util.ArrayList,并无实际类型参数信息
    }
}
复制代码
true
java.util.ArrayList
复制代码

4、泛型与反射

public class GenericTest {

    public static void main(String[] args) throws Exception {
        getParamType();
    }
    
     /*利用反射获取方法参数的实际参数类型*/
    public static void getParamType() throws NoSuchMethodException{
        Method method = GenericTest.class.getMethod("applyMap",Map.class);
        //获取方法的泛型参数的类型
        Type[] types = method.getGenericParameterTypes();
        System.out.println(types[0]);
        //参数化的类型
        ParameterizedType pType  = (ParameterizedType)types[0];
        //原始类型
        System.out.println(pType.getRawType());
        //实际类型参数
        System.out.println(pType.getActualTypeArguments()[0]);
        System.out.println(pType.getActualTypeArguments()[1]);
    }

    /*供测试参数类型的方法*/
    public static void applyMap(Map<Integer,String> map){

    }
}
复制代码
java.util.Map<java.lang.Integer, java.lang.String>
interface java.util.Map
class java.lang.Integer
class java.lang.String
复制代码
public static void main(String[] args) throws Exception {
		//定义一个包含int的链表
		ArrayList<Integer> al = new ArrayList<Integer>();
		al.add(1);
		al.add(2);
		//获取链表的add方法,注意这里是Object.class,如果写int.class会抛出NoSuchMethodException异常
		Method m = al.getClass().getMethod("add", Object.class);
		//调用反射中的add方法加入一个string类型的元素,因为add方法的实际参数是Object
		m.invoke(al, "hello");
		System.out.println(al.get(2));
	}

5 泛型的限制

5.1 模糊性错误

public class User<K, V> {
    
    public void show(K k) { // 报错信息:'show(K)' clashes with 'show(V)'; both methods have same erasure
        
    }
    public void show(V t) {

    }
}
复制代码

由于泛型擦除,二者本质上都是Obejct类型。方法是一样的,所以编译器会报错。

换一个方式:

public class User<K, V> {

    public void show(String k) {

    }
    public void show(V t) {

    }
}
复制代码

使用结果:

 

  可以正常的使用

 

5.2 不能实例化类型参数

编译器也不知道该创建那种类型的对象

public class User<K, V> {

    private K key = new K(); // 报错:Type parameter 'K' cannot be instantiated directly

}
复制代码

5.3 对静态成员的限制

静态方法无法访问类上定义的泛型;如果静态方法操作的类型不确定,必须要将泛型定义在方法上。

如果静态方法要使用泛型的话,必须将静态方法定义成泛型方法

public class User<T> {

    //错误
    private static T t;

    //错误
    public static T getT() {
        return t;
    }

    //正确
    public static <K> void test(K k) {

    }
}
复制代码

5.4 对泛型数组的限制

public class User<T> {

    private T[] values;

    public User(T[] values) {
        //错误,不能实例化元素类型为类型参数的数组
        this.values = new T[5];
        //正确,可以将values 指向类型兼容的数组的引用
        this.values = values;
    }
}
复制代码

5.5 对泛型异常的限制

泛型类不能扩展 Throwable,意味着不能创建泛型异常类。

标签:代码,java,void,public,类型,泛型,全解,class
来源: https://www.cnblogs.com/msjhw/p/15566798.html