Java中的泛化

在JDK1.5版本中提供了泛型概念,泛型实质上就是使程序员定义安全的类型,在没有提供泛型之前,Java也提供了对Object的引用“任意化”操作,这种任意化操作就是对Object引用进行“向下转型”及“向上转型”操作,但某些强制类型转换的错误也许不会被编译器捕捉,所有强制类型转换存在完全隐患,于是提供了泛型机制。

一、回顾“向上转型”与“向下转型”

通过以下示例来回顾:

public class Test {
	private Object b;
	public Object getB() {
		return b;
	}
	public void setB(Object b) {
		this.b = b;
	}
	public static void main(String args[]) {
		Test t =new Test();
		t.setB(new Boolean(true));
		System.out.println(t.getB());
		t.setB(new Float(3.14));
		Float f = (Float)(t.getB());
		System.out.println(f);
	}
}
/*输出结果如下:
true
3.14
*/

在上述示例中,Test类中定义了私有的成员变量b,它的类型为Object类型,同时为其第一轮相应的setXXX()与getXXX()方法。在类主方法中,将new Boolean(true)对象作为setB()方法的参数,由于setB()方法的参数类型使Object,这样就实现了“向上”转型。同时在调用getB()方法时,将getB()方法返回的object对象与相应的类型返回,这就是“向下转型”。

 

二、定义泛型

Object类为最上层的父类,很多程序员为了使程序更为通用。设计程序时通常使传入的值与返回值都以Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时会发生ClassCastException异常。

泛型的语法如下:

类名<T>
//T代表一个类型的名称

通过以下示例来说明泛型的定义:

public class OverClass<T> {
	private T over;
	public T getOver() {
		return over;
	}
	public void setOver(T over) {
		this.over = over;
	}
	public static void main(String args[]) {
		OverClass<Boolean> over1 = new OverClass<Boolean>();
		OverClass<Float> over2 = new OverClass<Float>();
		over1.setOver(true);
		over2.setOver(3.14f);
		Boolean b = over1.getOver();
		Float f = over2.getOver();
		System.out.println(b);
		System.out.println(f);
	}
}
/*输出结果如下:
true
3.14
*/

 

三、泛型的常规用法:

1、定义泛型类时声明多个类型

语法如下:

MutiOverClass<T1,T2>
//MutiOverClass:泛型类名称

其中,T1和T2为可能被定义的类型。

这样在实例化指定类型的对象时就可以指定多个类型,例如:

MutiOverClass<Boolean,Float> = new MutiOverClass<Boolean,Float>();

2、定义泛型类时声明数组类型

示例如下:

public class ArrayClass<T> {
	public T[] array;
	public void SetT(T[] array) {
		this.array = array;
	}
	public T[] getT() {
		return array;
	}
	public static void main(String args[]) {
		ArrayClass<String> a = new ArrayClass<String>();
		String[] array = {"成员1","成员2","成员3"};
		a.SetT(array);
		for(int i=0;i<a.getT().length;i++) {
			System.out.println(a.getT()[i]);
		}
	}
}
/*输出结果如下:
成员1
成员2
成员3
*/

通过上述示例可见,可以在使用泛型机制时声明一个数组,但是不可以使用泛型来建立数组的实例,例如,下面的代码就是错误的:

public class ArrayClass<T>{
    private T[] array = new T[10];
}

3、集合类声明容器的元素

可以使用K和V两个字符代表容器中的键值和与键值对应的具体值,示例如下:

public class TestClass<K,V> {
	public Map<K, V>m = new HashMap<K, V>();
	public void put(K k,V v) {
		m.put(k, v);
	}
	public V get(K k) {
		return m.get(k);
	}
	public static void main(String[] args) {
		TestClass<Integer, String> tc = new TestClass<Integer, String>();
		for(int i=0;i<5;i++) {
			tc.put(i,"集合成员:"+i);
		}
		for(int i=0;i<tc.m.size();i++) {
			System.out.println(tc.get(i));
		}
	}
}
/*输出结果如下:
集合成员:0
集合成员:1
集合成员:2
集合成员:3
集合成员:4
*/

在Java中有很多集合框架已经都被泛化了,可以在主方法中直接public Map<K,V> m = new HashMap<K,V>();语句创建实例,然后相应的调用Map接口中的put()与get()方法完成填充容器或根据键名获取集合中具体值的功能,下列几个时常用的被泛型化的集合类:

常用的被泛型化的集合类
集合类泛型定义
ArrayListArrayLiset<E>
HashMapHashMap<K,V>
HashSetHashSet<E>
VectorVector<E>


 

 

 

 

 

 

示例如下:


public class AnyClass {
	public static void main(String args[]) {
		ArrayList<Integer> a = new ArrayList<Integer>();
		a.add(1);
		for(int i=0;i<a.size();i++) {
			System.out.println("获取ArrayList容器的值:"+a.get(i));
		}
		Map<Integer, String> m = new HashMap<Integer, String>();
		for(int i=0;i<5;i++) {
			m.put(i,"成员:"+i);
		}
		for(int i=0;i<m.size();i++) {
			System.out.println("获取Map容器的值:"+m.get(i));
		}
		Vector<String> v = new Vector<String>();
		for(int i=0;i<5;i++) {
			v.addElement("成员"+i);
		}
		for(int i=0;i<v.size();i++) {
			System.out.println("获取Vector容器的值:"+v.get(i));
		}
	}
}
/*输出结果如下:
获取ArrayList容器的值:1
获取Map容器的值:成员:0
获取Map容器的值:成员:1
获取Map容器的值:成员:2
获取Map容器的值:成员:3
获取Map容器的值:成员:4
获取Vector容器的值:成员0
获取Vector容器的值:成员1
获取Vector容器的值:成员2
获取Vector容器的值:成员3
获取Vector容器的值:成员4
*/

四、泛化的高级用法:

1、限制泛型可用类型

默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类示例的类型错了限制,语法如下:

class 类名称<T extends anyClass>
//anyClass:某个接口或类

使用泛型限制后,泛型类的类型必须实现或继承了anyClass这个接口或类。无论anyClass是接口还是类,在进行泛型限制时都必须使用extends关键字,示例如下:

public class LimitClass<T extends List> {
	public static void main(String[] args) {
		LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
		LimitClass<LinkedList> l2 = new LimitClass<LinkedList>();
		//下面这句时错误的,因为HashMap没有实现List()接口
        LimitClass<HashMap> l3 = new LimitClass<HashMap>();
	}
}

2、使用类型通配符

在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时限制这个泛型类的类型实现或继承某个接口或类的子类,要声明这样一个对象可以使用"?"通配符来表示,同时使用extends关键字来对泛型加以限制,语法如下:

泛型类名称<? extends List>a = null;

3、继承泛型类与实现泛型接口

示例如下:

public class ExtendClass<T1> {
}
class SubClass<T1,T2,T3>extends ExtendClass<T1>{
}

如果在SubClass类继承ExtendClass类时保留父类的泛型类型,需要在继承时指明,如果没有指明,直接使用extends ExtendClass语句进行继承,则SubClass类张的T1、T2和T3都会自动变为Object,所有在一般情况下都将父类的泛型类型保留。

5、泛型总结

泛型的使用方法如下:

(1)泛型的类型参数只能时类类型,不可以时简单类型

(2)泛型的类型个数可以是多个

(3)可以使用extends关键字限制泛型的类型

(4)可以使用通配符限制泛型的类型
 

<think>好的,用户想了解Java机制,包括使用和原理。首先,我需要回忆一下Java的基本概念和常见用法,比如类参数、类、方法等。然后要深入到底层原理,特别是类擦除的相关知识,因为这是Java实现的关键。 用户提供的引用资料中提到,在编译后会进行类擦除,替换为Object或边界类,比如引用[1]和[2]都提到了这一点。需要解释类擦除的过程,以及它带来的限制,比如不能使用基本类作为类参数,无法进行运行时类检查等。 另外,引用[3]提到Class类在Java 5之后变成化的,这可能涉及到反射和的结合使用,比如获取信息的方法。引用[4]提到处理时可能用到的应急方案,比如反射和类常量,这里可能需要解释如何通过反射获取实际类,比如ParameterizedType的使用。 接下来要组织内容结构,先介绍的作用和使用方法,再深入原理部分,包括类擦除的具体例子,桥接方法的作用,以及在集合框架中的应用。然后讨论类擦除带来的限制,并给出使用建议,比如使用有界类参数、类通配符等。 用户可能还会关心为什么Java选择类擦除来实现,这可能与兼容旧版本Java有关,需要简要说明。同时,要提到即使编译时擦除了类信息,但通过反射仍然可以获取部分信息,比如字段的,这在引用[3]和[4]中有涉及。 最后,生成相关问题,考虑用户可能进一步想了解的内容,比如类擦除的优缺点,与C++模板的区别,如何绕过类擦除的限制等。确保问题覆盖原理、应用和高级话题,帮助用户深入理解。</think>### Java机制解析 #### 一、核心作用 1. **类安全**:编译时检查类匹配,避免$ClassCastException$[^2] 2. **代码复用**:编写可处理多种数据类的通用代码 3. **消除强制转换**:自动处理类转换,如集合框架中的元素存取 #### 二、基本使用形式 ```java // 类 class Box<T> { private T value; public void set(T t) { this.value = t; } } // 方法 public static <E> E getFirstElement(List<E> list) { return list.get(0); } // 通配符使用 List<? extends Number> numbers = new ArrayList<Double>(); ``` #### 三、底层实现原理 1. **类擦除(Type Erasure)**: - 编译后参数被擦除为Object或边界类 - 例如$List<String>$变为原始类$List$ - 自动插入强制类转换: ```java // 编译前 String s = list.get(0); // 编译后 String s = (String)list.get(0); ``` 2. **桥接方法(Bridge Methods)**: ```java interface Comparable<T> { int compareTo(T o); } // 编译生成 public int compareTo(Object o) { return compareTo((Integer) o); } ``` 3. **信息保留位置**: - 方法签名中的参数/返回类 - 继承关系中父类的参数 - 通过反射可获取字段的信息[^4] #### 四、关键限制与对策 | 限制类 | 解决方案 | |-----------------------|------------------------------| | 不能实例化 | 通过Class<T>参数传递构造器 | | 无法使用基本类 | 使用包装类 | | 类检查运行时失效 | 结合instanceof检查原始类 | | 方法重载冲突 | 修改方法签名或使用不同方法名 | #### 五、高级特性应用 1. **通配符边界控制**: ```java void process(List<? super Integer> list) // 下限 void copy(List<? extends Number> src) // 上限 ``` 2. **类推断优化**: ```java // Java7钻石操作符 List<String> list = new ArrayList<>(); // 链式调用类推断 process(Collections.emptyList()); ``` 3. **反射获取**: ```java Type type = field.getGenericType(); if (type instanceof ParameterizedType) { Type[] actualTypes = ((ParameterizedType)type).getActualTypeArguments(); } ``` #### 六、设计考量 1. **兼容性优先**:采用类擦除保持与Java5前版本的兼容 2. **性能零开销**:运行时无额外类检查开销 3. **渐进式改进**:通过$@SafeVarargs$等注解逐步完善
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值