为何要使用Java泛型

Java 泛型一直被编程办所诟病,但是其实它也有优点,这促使我们使用泛型。

假如有一个持有对象的容器类,我们想让它能够持有任何类型的对象,如下

public class Holder {
  private Object object;

  public void set(Object obj) {
    object = obj;
  }

  public Object get() {
    return object;
  }
}

Holder 类使用 Object 类的对象来保存任何类型的对象,这看起来很完美。

现在我们用这个 Holder 类来保存和获取对象

Holder h = new Holder();
// 保存String对象
h.set("hello");
// 获取String对象
String s = (String) h.get();

首先创建了一个 Holder 对象,然后用这个对象保存了一个 String 类型对象,最后获取保存的这个对象,由于我们知道保存的对象是String类型,因此这里可以强制转换,并且不会有任何问题。

然而如果我们把一个Holder对象传入一个方法,如下

public void test(Holder h) {
  // 具体的类型是什么呢?
  Object obj = h.get();
}

通过get()方法获取的对象是什么类型呢?你可以会认为是String类型,因为你刚才保存的就是这个类型,但是谁又能保存没有其它程序员,把这个Holder对象保存了其它类型对象呢? 如果存在这个情况,那么这里强制转换为String类型,就是出现异常。

然而,如果使用泛型,可以解决这个问题。我们通过泛型改写 Holder 类

public class Holder<T> {
	private T item;

	public void set(T t) {
		item = t;
	}

	public T get() {
		return item;
	}
	
	public static void main(String[] args) {
		Holder<String> h = new Holder<>();
		h.set("Hello");

		// 编译报错
		// h.set(1);
		
		// 不用执行强制类型转换
		String s = h.get();
	}
}

Holder<String> 保证了添加的元素只能是String类型,这个是由编译器保证的。

同时获取对象时,不用再手动执行强制类型转换,因为编译器会自动生成类型转换代码。

如果你查看字节码 Holder.class,就会发现一些端倪

public class Holder<T> {
    private T item;

    public Holder() {
    }

    public void set(T t) {
        this.item = t;
    }

    public T get() {
        return this.item;
    }

    public static void main(String[] args) {
        Holder<String> h = new Holder();
        h.set("Hello");
        String s = (String)h.get();
    }
}

首先创建Holder对象时,使用的是new Holder(),而不是new Holder<>(),这里可以看出泛型被擦除了。

其次,调用的 get() 方法,编译器自动帮我们加上了类型转换的代码。

其实,当调用有泛型参数的方法时,例如set(T t),编译器会检测传入参数的类型。当调用返回与泛型相关的类型时,例如T get(),编译器会自动添加类型转换代码。这两个动作被称之为编译器对泛型的边界动作,即对泛型参数的检测以及对泛型返回值的转型。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值