对Java泛型上限、下限的理解

泛型上限、下限所操作的运行时对象类型,永远是所给类型的子类型;
(1)上限保证返回值类型、对象能够调用的方法一定正确,但是编译器不会知道现在拿到的元素具体是哪一个子类型,既然不知道具体子类型,那也就无法将拿到的对象传入任何一个需要具体子类型的方法里面!为什么不行?因为编译器他不知道你传入的这个对象是不是你要调用的方法需要的子类型对象,万一你拿到的对象是方法需要的子类型的父类型对象或者同一个继承级别的不同子类型的对象,父类型对象不可能自动转型为子类型、不同子类型的对象自动转型该方法的子类型对象更不可能,这么一来,你不就会在运行时出现类型转换的异常了吗?所以就凭这一点,编译器就可以在编译期间报错。
(2)下限保证你拿到的对象的类型一定是你要调用的方法所需对象类型的子类型,相对而言:你拿到的对象就是子类型,你要调用方法的对象类型就是父类型;子类型向上自动转型为父类型,说的过去吧,那就没问题!但是,下限没法保证你拿到的对象到底是什么类型,只能知道你拿到的对象的类型是你所给类型的父类型,具体是什么?可能是Object、也可能是任意一个父类型,不清楚,那你既然偏要调用这种返回类型的方法的话,编译器都告诉你臣妾做不到了,不就只能编译期间报错了吗?

泛型上限:
        1.表示: ? extedns T,T表示具体类型;比如? extends String;
        2.(1)的示例:
                

public class Test{
    
    
	/**
	* 编译器可以从List<? extends T>中的到信息有两个:
	*  (1)List容器中的对象类型一定都是扩展自T类型,那么编译器
	*  调用List<? extends T>中返回元素对象的方法后,返回对象
	*  的类型一定是T类型;
	*  (2)List容器中的对象类型一定都是扩展自T类型,那么编译器
	*  可以确定:凡是在T类型中定义的公有实例方法,容器中的任何元素一
	*  定可以调用;
	*/

	public static <T> T test01(final List<? extends T> list){
    
    
		if(list != null && list.size() > 0){
    
    
			// 编译器肯定这句代码绝对没问题
			return list.get(0);
		}
		return null;
	}


	public Number test02(final List<? extends Number> list){
    
    
		if(list != null && list.size() > 0){
    
    
			// 编译器打包票,下面两句代码绝对没问题
			System.out.println(list.get(0).doubleValue());
			return list.get(0);
		}
		return null;
	}
	public static void main(String[] args){
    
    
		/**
	    *  (1)List容器中的对象类型一定都是扩展自T类型,那么编译器
	    *  调用List<? extends T>中返回元素对象的方法后,返回对象
	    *  的类型一定是T类型;
		*/
		List<Number> numberList = List.of(1, 2, 3, 2.2);
		// 编译器认为:返回的一定是Number类型,是不是Integer类型不清楚;
		// 编译器只知道List<? extends Number>里面装的一定是Number类型
		System.out.println(Test.test01(numberList).getClass());

		/**
		*  (2)List容器中的对象类型一定都是扩展自Number类型,那么编译器
	    *  可以确定:凡是在Number类型中定义的公有实例方法,容器中的任何
	    *  元素一定可以调用;
	    */
	    Test t = new Test();
	    t.test02(numberList);

		// 也可以这么调用
		// 此时test02方法中的?类型就是Integer类型,即:
		// ? extends Number == Integer extends Number
		// 承认上面等式成立,那就对了
		List<Integer> intList = List.of(1, 2, 3);
		t.test02(intList);
	}
}

2.泛型下限:
        1.表示: ? super T,T表示具体类型,比如? super String;
        2.(2)的示例:

public class Test{
    
    
	/**
	* 编译器可以从List<? super T>中得到一个信息:
	*  list里面存储的元素对象类型一定是T类型的父类型,把T类型的对象放入
	*  list中是一定正确的。
	*/
	

	public static void <T> test01(final List<? super T> list, final T t){
    
    
		if(list != null){
    
    
			// 编译器敢保证,这句代码绝对没问题
			list.add(t);
		}
	}

	public void test02(final List<? super Integer> list, final Integer t ){
    
    
		if(list != null){
    
    
			// 编译器以它的人格担保,下面代码绝对没问题
			list.add(t);
		}
	}

	public static void main(String[] args){
    
    
		/**
		* 容器里面存储的是你要放的类型的父类型,子类型变父类型,自动向上转
		* 型,完全没得问题。除了不知道具体类型是什么,简直奈斯到了极点。
		*/
		List<Number> numberList = new ArrayList<>;
		Test.test01(numberList, 2.2);
		Test.test01(numberList, 1);


		Test t = new Test();
		List<Integer> intList = new ArrayList<>();
		t.test02(intList, 1);
	
	}
}

Java不支持泛型数组!!!!!

Guess you like

Origin blog.csdn.net/D___H/article/details/121563155