• Java学习笔记--泛型


    一个泛型类就是具有一个或者多个类型变量的类。

    我们可以只关注泛型,而不会为数据存储的细节而烦恼 。

      

    java泛型(一)、泛型的基本介绍和使用 http://blog.csdn.net/lonelyroamer/article/details/7864531

    泛型的内部原理:类型擦除以及类型擦除带来的问题 http://blog.csdn.net/lonelyroamer/article/details/7868820

    java泛型(三)、通配符的使用 http://blog.csdn.net/lonelyroamer/article/details/7927212

    1.泛型类

    一个Pair类:

    public class Pair<T>{
    	private T first ; 
    	private T second;
    	
    	public Pair(){ first = null; second = null; }	
    	public Pair(T first , T second){ this.first=first;this.second=second; }
    	
    	public T getFirst(){ return first; }
    	public T getSecond(){ return second; }
    	
    	public void setFirst(T first){ this.first = first; }
    	public void setSecond(T second){ this.second = second; }
    	
    	public  void pPrint(){
    		System.out.println(getFirst()+" " +getSecond());
    	}
    }
    

    注:泛型类可有多个类型变量如  public class Pair<T,U>{...}

      

    //演示泛型类
    public class PairDemo {
    	public static void main(String[] args) {
    		Pair<String> p1 = new Pair();
    		System.out.println(p1.getFirst()+" "+p1.getSecond());
    		Pair<String> p2 = new Pair("lee","lei");
    		//写成Pair p2 = new Pair("lee","lei") 也可以
    		System.out.println(p2.getFirst()+" "+p2.getSecond());
    		
    		String[] words = {"hello" , "world" , "super","man"};
    		Pair<String> p3 = ArrayAlg.minmax(words); //获得最大、最小的字符串组成的键值对
    		p3.pPrint();
    	}
    }
    
    class ArrayAlg{
    	public static  Pair<String> minmax(String[] s_array){
    		if(s_array == null||s_array.length==0)
    			return null;
    		String min = s_array[0];
    		String max = s_array[0];
    		for(int i = 1 ; i < s_array.length ; i++){
    			if(min.compareTo(s_array[i]) > 0 ) min = s_array[i];
    			if(max.compareTo(s_array[i]) < 0 ) max = s_array[i];
    		}
    		return new Pair<String> (min,max);
    	}
    }
    

    运行结果:

    null null
    lee lei
    hello world

    2.泛型方法

    泛型方法可以定义在普通类中,也可以定义在泛型类中。这个方法是定义在普通类中的。

    class ArrayAlg{
    	
    	//方法返回中间位置元素
    	public static <T>  T  getMiddle(T[] s_array){
    		if(s_array == null||s_array.length==0)
    			return null;
    		return s_array[s_array.length/2];
    		
    	}
    }
    

    调用泛型方法时,在方法名前尖括号内放入具体的类型。

    但大多数情况下,可以省略,编译器有足够的信息推断出所调用的方法。

    public class PairMethodDemo {
    	public static void main(String[] args) {
    		String[] words = {"hello" , "world","baidu" , "super","man"};
    		String s_middle = ArrayAlg.<String>getMiddle(words);	//可写成ArrayAlg.getMiddle(words);
    		System.out.println("位置在 中间的元素:"+s_middle);
    		
    		/* 注意 不能用int[] nums = {...};
    		 * 泛形要求能包容的是对象类型
    		 * 而基本类型在java里不属于对象
    		 * 因此,想要使用基本类型就用其包装类实现功能;
    		 * */
    		Integer [] nums = {2,52,6,8,4,2,6,10,7};
    		Integer n_middle = ArrayAlg.getMiddle(nums);
    		System.out.println("位置在中间的数字元素:"+n_middle);
    	}
    }  
    

    运行结果:

    位置在 中间的元素:baidu
    位置在中间的数字元素:4

    3.变量类型的限定

     有时,类或方法需要对类型变量T加以约束,例子:

    class ArrayAlg{
    	public static <T> T min(T[] arr){//编译错误	
    		if(arr==null||arr.length==0){
    			return null;
    		}
    		T smallest = arr[0];
    		for(int i = 0 ; i < arr.length ; i++){
    			if(smallest.compareTo(arr[i])>0)
    				smallest = arr[i];
    		}
    		return smallest;
    	}
    

      这个例子存在一个问题:smallest类型为 T, 这意味着它可以为任意类型的对象,但是T类型的对象不一定有compareTo方法。

    解决方法:通过对类型变量T设置限定.

    我们知道所有实现Comparable接口的类都会有compareTo方法,所以可以对T做如下限定:

    public static <T extends Comparable> T min(T[] arr){...}

    现在,泛型的min方法只能被实现了Comparable接口的类(如String、Date等)的数组调用。 

    注:

    1、不管该限定是类还是接口,统一都使用关键字 extends 

    2、可以使用&符号给出多个限定,比如

     

    注:为什么使用extends而不使用implements 

    T和绑定类型可以使类,也可以是接口,选择extends关键字的原因是更接近子类的概念。

    并且Java的设计者不打算在语言中添加一个新的关键字。

     一个变量类型或者通配符可以有多个限定: T extends Comparable & Serializable

    约束与局限性:

    (1)不能用基本类型实例化类型参数(不能用类型参数代替基本类型)

    (2)运行时类型查询制适用于原始类型
    如:

    if(a instanceof Pair<String>)  //ERROR

    实际上仅仅测试a是否是任意类型的一个Pair。下面的测试同样如此

    if(a instanceof Pair<T>)  //ERROR

    或强制类型转换:

    Pari<String> p = (Pari<String>) a;  //Warring,can only test "a" is a "Pair"

    要记住这一风险,无论何时使用instanceof或涉及泛型类型的强制类型转换表达式都会看到一个编译器警告。

    同样的道理,getClass方法总是返回原始类型。例如:

    Pair<String> stringPair = ...;

    Pari<Employee> employeePair = ...;

    if(stringPair.getClass()==employeePair.getClass())//they are equal

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

    如:Pair<String>[] table = new Pair<String>[10]  //ERROR

    这有什么问题呢?擦除之后,table的类型是Pair[]。可以把它转换为Object[];

    Object[] objarry = table;

    数组会记住它的元素类型,如果试图存储其他类型的元素,就会抛出一个ArrayStoreException异常;

    objarry[0] =new Pair<Employee>();能够通过数组存储检查,不过仍会导致一个类型错误。出于这个原因,不允许创建参数化类型的数组。

    需要说明的是,只是不允许创建这些数组,而生命类型为Pari<String>[]的变量仍然是合法的。不过不能用new Pair<String>[10]初始化这个变量。

    如果需要手机参数化类型对象,只有一种安全而有效的方法:使用ArrayList: ArrayList<Pair<String>>.

    (4)Varargs警告

    问题:向参数个数可变的方法传递一个泛型类型的实例。

    考虑下面的方法,它的参数个数是可变的

    public static <T> void addAll(Collection<T> coll , T... ts){
    	for(T t : ts) coll.add(t);
    }
    

    为了调用这个方法,Java虚拟机必须建立一个Pair<String>数组,这就违反了前面的规则。

    不过对于在这种情况,规则有所放松,只会得到一个警告,而不是错误。

    调用的例子:

    public class VarArgs {
    
    	public static void main(String[] args) {
    		List<Pair<String>> table = new ArrayList();
    		Pair<String> pair1 = new Pair("S1First","S1Second");
    		Pair<String> pair2 = new Pair("S2First","S2Second");
    		addAll(table,pair1,pair2);
    		//上一行代码有警告:Type safety: A generic array of Pair<String> is created for a varargs parameter
    		for(int i = 0 ; i < table.size() ; i ++){
    			table.get(i).pPrint();
    		}
    	}
    }
    

    运行结果:

    S1First S1Second
    S2First S2Second

    (5)不能实例化类型变量

    不能使用像new T(...),new T[...]或T.class这样的表达式中的类型变量。

    如下面的Pair<T>构造器就是违法的:

    publoic Pair(){ first = new T() ;seconde = new T(); }  //ERROR;

    类型擦除将T改变成Object,且本意肯定不希望调用 new Object().

    但是可以通过反射调用Class.newInstance方法来构造泛型对象。

     

    (6)不能抛出或捕获泛型类的实例

    拓展Throwable也是不合法的

    以下定义不能正常编译:

    public class Problem<T> extends Exception{/* ... */} //ERROR

    catch子句中不能使用类型变量。

    public static <T extends Throwable> void doWork(Class<T> t){
      try{
        do work
      }catch(T e){ //ERROR can't catch type variable
      }
    }
    

      

    (7)注意擦除后的冲突

    当泛型类型被擦除时,无法创建引发冲突的条件。

    public class Pait<T>{
      public boolean equals(T value){
        return first.equals(value)&&second.equals(value);
      }
      ..... }

    考虑一个Pair <String>。从概念上讲,它有两个equals方法:

    boolean equals(String)//defined in Pair<T>

    boolean equals(Object)//inherited from Object

    但是,直觉吧我们引入歧途。

    方法擦除 boolean equals(T)

    就是 boolean equals(Object)

    与Object.equals冲突

    补救的方法是重命名引发错误的方法。

  • 相关阅读:
    axios baseURL
    TP5 nginx 配置
    Vue
    key
    curl openssl error
    vue use bulma
    《平凡的世界》
    《听听那冷雨》余光中
    心烦意乱
    祝你19岁生日快乐
  • 原文地址:https://www.cnblogs.com/gnivor/p/4260705.html
Copyright © 2020-2023  润新知