五分钟了解Java泛型(下)

泛型方法

泛型方法定义

  • 泛型类是说明这个类有类型变量,在创建这个类对象时需要给类型变量赋值。泛型方法是说明这个方法有类型变量,在调用这个方法时需要给类型变量赋值。
//返回值为T类型的集合
public <T> T get(T[] ts, int index) {
  return ts[index];
}
  • get()方法是一个泛型方法,它有一个类型变量T,这说明在调用get()方法时需要给get()方法的T赋值。
    如果要定义一个有意义的泛型方法,那么:
  1. 参数需要使用类型变量;
  2. 返回值需要使用类型变量。
    所以,通常在调用泛型方法时,只需要传递参数就可以了,例如:
String[] strs =String s = o.get(strs, 0);

上面代码中给get()方法的类型变量T赋值为String,因为传递的参数为String数组,所以就是给T赋值为String。当然,也可以显示给出类型变量的值:o.<String>get(strs,0),在点后面,方法名前面给出类型值,但一般人不会这么打代码。

泛型的边界

  • 编译期状态:编译期状态,例如内部类!内部类就是只有编译器知道,而JVM不知道什么叫内部类!

泛型的擦除

  • 泛型其实是编译期状态,即JVM不知道什么是泛型,在JVM那里所有类都是正常的类。没有类型变量。一切的一切都是编译器干的好事儿!
  • 其实List的get()方法返回值还是Object,只是编译器帮我们添加了强转的语句。但这个强转一定不会出现问题。因为本来add()方法添加元素已经限制过了,那么在get()时,一定不会出现强转的问题。也就是说,在ArrayList类中持有的还是Object类型,而不是我们指定的类型。当然,就算是JVM没有泛型,但编译器会帮我们完成这些问题,我们就可以当泛型真的存在。

泛型边界限定的类型值的范围

  • 通常我们看到的泛型类都没有边界限定,也就是说可以给泛型类的类型变量赋任意类型的值(当然基本类型是不可以的)。
  • java允许给类型变量指定边界,这样用户在给类型变量赋值时就必须在边界之内。public class A<T extends Number> {}表示用户可以给T赋值为Number或Number的子类型。例如:new A<Integer>()这是可以的,但new A<String>()是不可以的。

通配符

通配符的作用

Object[] objs = new String[10];
objs[0] = new Integer(100);
  • 上面代码编译是可以通过的,但在运行时会出现ArrayStoreException。因为objs数组真实的身份是String[],向String[]数组中存放Integer对象当然是不行的。
ArrayList<Object> list = new ArrayList<String>();
list.add(new Integer(100);
  • 上面代码在第一行位置编译失败,因为泛型根本就不让把ArrayList<String>赋值给ArrayList<Object>,对于ArrayList<Object>而言,只能赋值ArrayList<Object>,其他的什么都不能赋值。
    也说明一个问题:
public static void printList(List<Object> list) {}
  • 调用printList()方法只能传递给它List类型的参数,而不能传递List,或者List,这说明我们的printList()方法有很多的限制,不够通用!!!你可能会想我再重载几次printList()方法吧,但这是行不通的!
public static void printList(List<Object> list) {}
public static void printList(List<String> list) {}
  • 因为JVM不知道什么是泛型,这两个方法在到了JVM那里时都是会把泛型参数擦除,这两个方法就是相同的方法了,擦除之后即:
public static void printList(List list) {}
public static void printList(List list) {}
  • 当然JVM不可能看到这样的代码,因为编译器不能让你编译通过!处理这个问题需要使用通配符!

子类通配符

public static void printList(List<? extends Person> list) {}
  • 这回可以传递给printList()方法List<Student>,以及List<Teacher>参数了。只要类型参数为Person,或者是Person子类型就都可以。
  • 你可以这样来理解通配符,通配符表示“不知道”的意思。即一个问号!但子类型通配符还是知道一些信息的,它只知道用户转递的类型参数一定是Person的子类型。虽然使用了通配符之后printList()方法更加通用了,但是这也是要付出一些代价的。因为不知道List中类型参数的真实类型,所以就不能调用list的add()方法了。你可能会想add(new Student())应该是可以的,但如果List是List<Teacher>呢,你怎么向这样的List添加Student呢?再想一想,add()方法已经作废了,什么都传递不了。

父类型通配符

public static void printList(List<? super Student> list) {}
  • 可以传递给printList()方法List<Student>,以及List<Person>,甚至List<Object>也是可以的。只要传递给printList()方法的List类型参数为Student,或者Student的父类型就是可以的。
  • 你现在可以向list中添加Student类型的参数,例如:list.add(new Student())。因为用户提供的参数List<Student>List<Person>List<Object>,无论哪一种类型,对于list.add(new Student())都是合法的。但是,现在我们不知道list的get()方法返回的是什么了!因为用户传递的可能是List<Student>List<Person>List<Object>,如果我们用Student s = list.get(0),那么如果list其实是一个List<Person>岂不是出错了!没错,只能使用Object来接收list.get(0)的返回值了。

无界通配符

  • 所谓无界通配符,即List<?>,对通配符没有限定。你可以给List<?>赋任意的值,但是,你能使用这样的list干什么呢?也不能add(),也只能使用Object来接收get()方法返回值。
  • 所以,通常List<?>只能表示你在使用泛型而已!编译器会认为List<?>比List更加优雅一些!你可能也看出来了,泛型还真是很垃圾!!!这也是没有办法中的办法,现在的泛型是迁移性兼容的一种版本而已!Java设计者不敢让JVM知道泛型的存在,原因是为了兼容1.4之前的版本。当所有人都在使用1.5以上版本的JDK后,Java的泛型可能就不会再是编译期状态了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值