Java泛型

参考Java核心第一卷 基础知识

1.泛型解决的主要问题

(1)可以被重用;

(2)更好的可读性和安全性

 

2.泛型程序设计使用

(1)仅仅使用泛型类,比如 List<String> list = new ArrayList<String>

(2)简单泛型类定义,比如  class SortedUtil<T> { }

(3)泛型类型限定和通配符:extends 子类型限定,super 超类型限定,比如

/**
 * 定义泛型类 SortedUtl<T>,* 注意类型参数 T 
 * (1)可以指定子类限定类型:比如 extends A&B&C 这里面 A,B,C代表多类或者接口
 * 但是根据 extends 关键字我们知道:只能继承自一个类,因此 A、B、C...中只能有一个类
 * 并且如果确实有这么复杂的限定类型,那么这个类还必须放在第一位置!
 * 
 * (2)还可以使用 super 指定超类限定类型
 * 
 * (3)也可使用 ? 通配符
 *
 * @param <T>
 */
class SortedUtl<T extends Comparable<? super User> & Serializable> {
	
}

 

3.泛型擦除

List<String> 告诉编辑器:列表参数使用String类型。但对于JVM来说:这是不存在的

JVM中不存在泛型类和泛型方法,任何限定类型都被擦除,原则是

(1)如果没有指定的限定类型 比如List<?> 那么使用Object替换;

(2)如果有限定类型,那么使用限定类型边界的第一个替换。比如  Demo<T extends A & B &C> 那么使用 A来替换

JVM 根据这一原则对泛型类、泛型方法进行翻译。这有可能导致出现一些情况,比如JVM中会存在两个相同的方法签名

针对这种情况,JVM会生成桥方法,保证类的多态性。总之有如下规律

  • 任何泛型在编译过程中就被擦除类型,不管是泛型类、泛型方法、泛型表达式
  • 所有类型参数都被 [限定类型]列表中第一个替换,如果没有指定限定类型,则用Object替换
  • 桥方法被合成用于保持Java多态性
  • 为保证类型安全性,必要时插入强制类型转换

4.泛型的约束

(1)泛型类:类型参数不能使用基本类型

(2)运行时类型:getClass 或者  instanceof 操作,都不能获取到限定类型,因为类型擦除的原因

(3)泛型类不能扩展异常类. 注意: 类型参数是可以扩展的,是泛型类不能扩展

(4)泛型数组:不能声明参数化的泛型类型数组.(注意:是不能声明参数化的)

(5)泛型类:类型参数不能实例化

(6)不能声明静态化的限定类型

(7)注意些特殊的写法

package simple;

import java.lang.reflect.Array;
import java.util.Date;

public class Pair2<T> {

	private T max;

	private T min;

	public Pair2(T max, T min) {
		this.max = max;
		this.min = min;
	}

	public String toString() {
		return String.format("{max:%s,min:%s}", this.max, this.min);
	}
	
	public static void main(String[] args) {
		// 1.泛型类:类型参数不能使用基本类型
		// 类型擦除之后,基本类型被Object替换,比如 Pair2<Object>
		// 而Object只能存储Double这种包装类的引用,不能存储double基本类型的值
		// 因此泛型类不能使用基本类型
		// Pair2<int> pair = new Pair2(10, 2);
		
		// 2.运行时类型
		// getClass 或者  instanceof 操作,都不能获取到限定类型,因为类型擦除的原因
		Pair2<Integer> pair1 = new Pair2<Integer>(10, 1);
		Pair2<String> pair2 = new Pair2<String>("z", "a");
		// 打印出来都是 class.simple.Pair2 也是因为类型擦除的原因
		System.out.println(pair1.getClass());
		System.out.println(pair2.getClass());
		
		// 3.泛型类不能扩展异常类. 注意: 类型参数是可以扩展的,是泛型类不能扩展
		//class Test<U> extends Throwable{
		// }
		
		// 在catch中捕获限定类型的对象也是不允许的
		// class Test<U extends Throwable> {
		//	{
		//		try{
					
		//		}catch(U u) {

		//		}
				
		//	}
		// }
		
		// 在方法中可以使用限定类型变量,但一样的不允许在catch中捕获
		class Test<U extends Throwable> {
			public void test(U u) {
				try{
					
				// catch 捕获泛型对象仍然是错误的
				// } catch(U e) {
					
				} catch (Throwable e) {
					try {
						// 使用方法变量
						throw u;
					} catch (Throwable e1) {
						e1.printStackTrace();
					}
				}
			}
		}
		
		// 4.泛型数组:不能声明参数化的泛型类型数组.注意:是不能声明参数化的
		// Pari2<String>[] pp = new Pair2<String>[10];
		// 也是因为类型擦除,导致 Pair2<Stirng>被Pair2<Object>替换
		// 那么实际上就是 Pair2<Object> pp = new Pair2<Object>[]
		// 这样一来也可以转换成 Object[] objPair = pp;
		// 而Object[]可以这样 objPair[0] = "haha"; 实际上你要求数组存储的是  Pair 类型,这样一来运行过程肯定报错
		
		// 就类似于下面这一段
		Integer[] ii = new Integer[10];
		Object[] objII = ii;
		// 编译虽然不会报错,但实际上是无法运行的:因为 ii 是Integer类型,所以objII也是Integer类型
		// 这是因为:Java是强类型语言,要求数组记住元素类型,并且数组元素类型必须一致
		//objII[0] = "hello";
		// System.out.println(objII[0]);
		
		// 除非Object类型数组指向的本身就是 Object[]类型的地址引用
		Object[] obj2 = new Object[2];
		obj2[0] = "str";
		obj2[1] = new Date();
		System.out.println(obj2[0].getClass() + "---" + obj2[1].getClass());
		
		// 也可以利用反射创建泛型数组
		Pair2<String>[] pairs =  (Pair2<String>[]) Array.newInstance(Pair2.class, 10);
		pairs[0] = new Pair2<String>("z", "a");
		System.out.println(pairs);
		// 或者非参数化的泛型数组也可以定义
		Pair2[] pairs2 = new Pair2[2];
		
		// 5.泛型类:类型参数不能实例化
		class Test1<T>{
			public void test(T t){
				// 使用限定类型实例化不允许
				// T tt = new T();
				
				try {
					// 但是利用反射可以这样实例化泛型对象
					T tt = (T)t.getClass().newInstance();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
			
			// 6. 也不能声明静态化的限定类型
			// static T t;
			
			// 7. 也不能这么写:因为限定类型被擦除后,本质上就变成了  public boolean equals(Object t)
			// 因为所有类都是Object的子类,意味着  Test1 本身就有了一个 equals(Object t) 方法
			// 因此这么些是不合法的
			/*
			public boolean equals(T t) {
				return false;
			}
			*/
		}
		
		class Test2 {
			// 针对上述第7点,这么写是合法的,这是覆盖父类的方法
			// 而如果是用限定类型区作为equals参数,则导致 Test1 类中相当于有2个这样的方法
			public boolean equals(Object t) {
				return false;
			}
		}
		
	}
}


 


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值