Java Generic (1)

本文深入讲解Java泛型的概念,包括泛型类、泛型方法及擦除机制等核心内容,并探讨了它们在实际编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 泛型类
    泛型允许对类型进行抽象,最常见的泛型类是容器类。例如:

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for(String s: list) {
	System.out.println(s);
}

    以上例子中,如果试图向list中添加一个Integer对象,那么会导致编译错误。编译器会进行类型检查,这避免了使用非泛型容器类时常见的强制类型转换。泛型类是具有一个或者多个类型变量(type variable)的类。以下是个简单的例子:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Bag<E> {
	private List<E> bag = new ArrayList<E>();

	public Iterator<E> iterator() {
		return this.bag.iterator();
	}

	public boolean add(E e) {
		return this.bag.add(e);
	}

	public boolean remove(Object o) {
		return this.bag.remove(o);
	}
	
	public boolean contains(Object e) {
		return this.bag.contains(e);
	}

	public boolean addAll(Bag<? extends E> b) {
		return this.bag.addAll(b.bag);
	}
	
	public boolean removeAll(Bag<?> b) {
		return this.bag.removeAll(b.bag);
	}

	public boolean containsAll(Bag<?> b) {
		return this.bag.containsAll(b.bag);
	}
    
	public void clear() {
		this.bag.clear();
	}
   
   	List<E> getBag() {
		return this.bag;
	}
}
import java.util.Collections;
import java.util.Comparator;

public class Bags {
	public static <E> void sort(Bag<E> b, Comparator<? super E> c) {
		Collections.sort(b.getBag(), c);
	}

	public static <T extends Comparable<? super T>> T max(Bag<T> b) {
		return Collections.max(b.getBag());
	}

	public static <E> void copy(Bag<E> dest, Bag<? extends E> src) {
		dest.addAll(src);
	}
}

    需要注意的是,不能抛出或者捕获泛型类的实例,泛型类继承自Throwable也是不合法的。此外也不能声明泛型类实例的数组,例如以下代码会造成编译错误:

Bag<Integer> b3[] = new Bag<Integer>[10]; // Cannot create a generic array of Bag<Integer>

 

2 泛型方法
    泛型方法是指带有类型参数的方法,类型变量的位置是修饰符和返回类型之间。在调用泛型方法的时候,具体类型的位置是方法名前的尖括号中,在绝大多数的情况下,由于编译器可以判断出具体类型,因此也可以省略具体类型。以下是个简单的例子:

public class Base {
    public String getName() {
	    return "Base";
    }

    public static <T> void print(T t) {
        System.out.println(t.toString());
    }
 
    public static void main(String args[]) {
	    Base base = new Base();
	    Base.<Base>print(base);
	    Base.print(base);
    }
}

    以上例子中的print方法内只能调用T从Object类继承的方法。如果希望将T限定为Base及其子类,那么可以使用extends设定上限,或者使用super设定下限(C++中不能限定参数变量的类型),例如:

public static <T extends Base> void print(T t) {
	System.out.println(t.getName());
}

    类型变量也可以有多个限定,限定类型用&分割。如果限定类型可以包含多个接口,但是至多有一个类(单继承),如果有一个类,那么它必须是第一个限定类型。例如: 

T extends Comparable & Serializable

 

3 擦除

    编译器会为每个泛型类型自动提供一个原始类型(raw type)。其类型变量会被擦除(因此Java泛型没有C++模板类的代码膨胀问题),并用其限定类型代替,如果没有限定类型,那么使用Object。不能使用原始数据类型作为类型变量,例如没有Bag<int>,但是可以有Bag<Integer>。 由于类型变量会被擦除,因此不管泛型类的类型变量是什么,它的所有实例的运行时类是相同的,例如以下代码的输出如下:

Bag<Integer> b1 = new Bag<Integer>();
Bag<String> b2 = new Bag<String>();
System.out.println(b1.getClass());
System.out.println(b2.getClass());
System.out.println(b1.getClass() == b2.getClass());

    class Bag
    class Bag
    true

    由于类的静态成员变量和成员方法也被类的所有实例共享,因此不能在类的静态成员变量和成员方法中引用类型变量。例如以下的代码会导致编译错误:

public class Bag<E> {
	private static E INSTANCE; // Cannot make a static reference to the non-static type E

	public static E getInstance() { // Cannot make a static reference to the non-static type E
		return INSTANCE;
	}
}

    不能通过类型变量构造实例,但是可以通过Class.newInstance和Array.newInstance来构造实例。例如以下的代码会造成编译错误:

public Bag() {
	E e1 = new E(); // Cannot instantiate the type E
	E e2[] = new E[10]; 
}

    在擦除后,也不能引起冲突。考虑如下代码:

public class Bag<E> {
	public boolean equals(E e) { // Name clash: The method equals(E) of type Bag<E> has the same erasure as equals(Object) of type Object but does not override it
		return true;
	}
}

    以上的Bag类的类型变量被擦除后的代码如下:

public class Bag<E> {
	public boolean equals(Object e) {
		return true;
	}
}

    其equals方法与Object.equals(Object obj)方法冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值