java 泛型转换_Java泛型—类型转换

如下代码编译无法通过:

classA{}class B extendsA {}public static void funC(ListlistA) {//...

}public static void funD(ListlistB) {

funC(listB);//...

}

Unresolved compilation problem: The method doPrint(List) in the type test is not applicable for the arguments (List)

而下面的代码就没问题:

public static voidfunC(A a) {//...

}public static voidfunD(B b) {

funC(b);//...

}

在第二段代码中,类型B的实例向上转换成类型A的实例传入函数funC(A a),这是正常的隐式类型转换。而第一段代码则表明类型List的实例无法转换成类型List的实例。这就引出了Java泛型的类型转换问题。

Java是在JDK 5中引入的泛型(generics)新特性的。最主要的应用是在JDK 5中的新集合类框架中。可以解决之前的集合类框架在使用过程中出现的运行时刻类型错误,把问题暴露在编译中。但是为了保证与旧版本的兼容性,Java泛型的实现采用了类型擦除的机制,带来了一些缺陷。Java泛型的分析参考:http://www.infoq.com/cn/articles/cf-java-generics/。

Java泛型的实现:类型擦除

以上问题在C++中不会出现,这是两种语言的泛型实现机制不同造成的。

C++中的泛型类或方法,编译器会自动检查实例化的参数类型,然后根据类型检查参数类型的调用是否合理。如下代码能很好说明这个问题,模板在定义时并不对参数类型T的操作提前限制,等模板实例化时会根据T的具体类型检查操作是否合理。

template classManipulator {

T obj;public:

Manipulator(T x) {obj=x; }voidmanipulate() {obj.f(); }

};classHasF {voidf() {//...

}

};intmain() {

HasF hf;

Manipulatormanipulator(hf);

manipulator.manipulate();

}

Java采用的是所谓的“擦除”机制。在编译时不考虑传入的类型,把该类型的印迹全部擦除,视该类型为最基本的Object类型。但是Java会在编译时检查泛型接口传入的类型参数的实例是否存在隐含的转换情况,为了安全,泛型禁止此类转换。这就是开始我们遇到的问题,编译器检测到需要把List实例转换成List实例,这是被禁止的。明明可以实现的类型转换却被禁止,原因是编译时把类型参数擦除,当做Object,在运行时还要把Object类型的实例转换成类型参数的实例,两次转换存在运行时错误的风险。

为了解决泛型中隐含的转换问题,Java泛型加入了类型参数的上下边界机制。 extends A>表示该类型参数可以是A(上边界)或者A的子类类型。编译时擦除到类型A,即用A类型代替类型参数。这种方法可以解决开始遇到的问题,编译器知道类型参数的范围,如果传入的实例类型B是在这个范围内的话允许转换,这时只要一次类型转换就可以了,运行时会把对象当做A的实例看待。

引入上边界使Java泛型具有了像C++泛型那样在模板中调用类型参数的方法的能力。如下Java代码,实现了上面C++代码同样的功能:

class Manipulator{

T hf;public:

Manipulator(T t) {hf=t; }voidmanipulate() {hf.f(); }

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,泛型是一种强类型机制,它可以让你在编译时检查类型错误,从而提高代码的安全性和可读性。在使用泛型时,我们经常会遇到父类和子类的泛型转换问题。 首先,我们需要明确一点:子类泛型不能转换成父类泛型。这是因为Java中的泛型是不协变的。例如,如果有一个类A和它的子类B,那么List<A>和List<B>之间是不存在继承关系的。 下面我们来看一个例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 } } ``` 在这个例子中,我们定义了Animal类和它的子类Dog。然后我们定义了两个List,分别是List<Animal>和List<Dog>。如果将List<Dog>赋值给List<Animal>,会出现编译错误。这是因为List<Animal>和List<Dog>之间不存在继承关系。 那么,如果我们想要让子类泛型转换成父类泛型,应该怎么办呢?这时我们可以使用通配符来解决问题。通配符可以表示任意类型,包括父类和子类。例如,我们可以将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 下面我们来看一个使用通配符的例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 List<? extends Animal> list3 = new ArrayList<>(); list3 = list2; // 正确 } } ``` 在这个例子中,我们定义了List<? extends Animal>来表示任意继承自Animal的类型。然后我们将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 总结一下,Java中的泛型是不协变的,子类泛型不能转换成父类泛型。如果需要实现子类泛型转换成父类泛型,可以使用通配符来解决问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值