泛型在类、接口和方法中定义,在实现(extends,implements)和实例化时使用。
定义类或接口时,使用"<E extends Fruit>"这种形式,之后就可以在类中对E进行操作。
定义方法所接收的参数时,使用"List<? extends Fruit>"这种形式,就可以接收这个范围的List做参数。
实例化时,不能使用问号这种形式来指定泛型——不能new List<? extends Apple>();
继承或实现时,也不能使用问号这种形式来指定泛型——不能public interface MyList extends List<? extends Apple>
关于extends和super关键字的PECS(producer-extends, consumer-super)原则:
如果参数化类型表示一个T生产者,就使用<? extends T>,因为它只能get,用于将数据从生产者取出(只要生产者可以生产T的子类,那就一定可以生产T);
如果参数化类型表示一个T消费者,就使用<? super T>,因为它只能add,用于将数据add入消费者(只要消费者可以消费T的超类,那就一定可以消费T)。
泛型的继承原则:
1,相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。即List<String>是Collection<String> 的子类型,List<String>可以替换Collection<String>。这种情况也适用于带有上下界的类型声明。
2,当泛型类的类型声明中使用了通配符的时候, 其子类型可以在两个维度上分别展开。如对Collection<? extends Number>来说,其子类型可以在Collection这个维度上展开,即List<? extends Number>和Set<? extends Number>等;也可以在Number这个层次上展开,即Collection<Double>和 Collection<Integer>等。如此循环下去,ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子类型。
3,如果泛型类中包含多个类型参数,则对于每个类型参数分别应用上面的规则。
泛型的定义原则:
1,泛型类与一般的Java类基本相同,只是在类和接口定义上多出来了用<>声明的类型参数。
2,一个类可以有多个类型参数,如 MyClass<X, Y, Z>。
3,每个类型参数在声明的时候可以指定上界。
4,所声明的类型参数在Java类中可以像一般的类型一样作为方法的参数和返回值,或是作为域和局部变量的类型。
5,由于类型擦除机制,类型参数并不能用来创建对象或是作为静态变量的类型。
如果没有向后兼容性顾虑,那么Collection框架会这么设计(而实际上不是):
interface Collection<E> {
public T[] toArray(Class<T super E> elementClass);
}
这样很通用,假设E是Apple类,那么传入任何一个Apple的父类(例如Fruit)都合法,会返回Fruit数组。
如果传入Object,那就相当于现在Collection接口的Object[] toArray()方法了。
同样,如果没有向后兼容性顾虑,那么Collection框架的remove和removeAll会这么写(而实际上不是):
interface Collection<E> {
public boolean remove(E e); // not really
public void removeAll(Collection<? extends E> c); // not really
}
缺陷是:只能传入当前泛型的兼容类型,不能传入任意Object或Collection<Object>——这样无法到兼容之前的API定义。
不能创建泛型类型的对象,因为编译器不知道要调用什么构造函数。如果泛型类需要构造用泛型类型参数来指定类型的对象,那么构造函数应该接受类文字(Foo.class)并将它们保存起来,以便通过反射创建实例(类似于上述Collection的toArray方法)
类型安全的异构容器(内部用Map<Class<?>,Object>来实现):
public class Favorites {
public <T> void putFavorite(Class<T> type,T instance);
public <T> T getFavorite(Class<T> type);
}
例如用于数据库不定数目的columns:每个cell的数据都以这种方式指定类型保存Favorites到容器中,再将此容器当作元素保存到更上层的结果容器中。
不能实例化泛型类型的数组:new List<String>[3]是不合法的。
但是在可以确定类型的情况下,将Object数组强制转换成泛型数组是合法的,例如ArrayList的源码:return (T[]) Arrays.copyOf(elementData, size, a.getClass());
泛型方法:
声明方法时将用到的类型参数列在前面即可(所在类上不需要定义此参数类型):
public static <E> Set<E> union(Set<E> s1,Set<E> s2) ;
静态工具方法特别适合这样写。
一般来说编写java泛型方法时,返回值类型和至少一个参数类型应该是泛型,而且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,这个泛型方法的使用就大大的限制了,基本限制到跟不用泛型一样的程度。
第一种:public static <T extends CommonService> T getService(Class<T> clazz);
NoticeService noticeService=CommonService.getService(NoticeService.class);//正确的使用第一种泛型方法,不会出现编译错误。
NoticeService noticeService=CommonService.getService(UserService.class);//不正确的使用第一种泛型方法,会出现编译错误。
第二种:public static <T> T getService(Class<? extends CommonService> clazz);
NoticeService noticeService=CommonService.getService(NoticeService.class);//正确的使用第二种泛型方法,不会出现编译错误,逻辑也正确,运行时不会出现异常。
NoticeService noticeService=CommonService.getService(UserService.class);//不正确的使用第二种泛型方法,不会出现编译错误,但逻辑不正确,运行时会出现异常,危险!
--
网上不少关于泛型的说明很好,可是不知道是不是jdk版本问题,居然没办法实际通过编译,于是我只好自己在jdk1.7下一一重写和尝试,最后加上总结和注释,如下:
public class Apple extends Fruit {
}
public class Basket{
//从泛型化的list中取出Fruit,里面的元素一定是Fruit的子类,所以都可以当作Fruit返回
public static Fruit getFruit(List<? extends Fruit> list){
return list.get(0);
}
//将Apple放入泛型化的list(这个list可以接收的类型是Apple的父类,所以一定可以接收apple或apple的子类)
public static void addApple(List<? super Apple> list,Apple apple){
list.add(apple);
}
}
public class GenericTester extends AbstractTester{
public void test1(){
List<Apple> appleList = new ArrayList<Apple>();
appleList.add(new Apple()); //先定义List<Apple>,填入元素
Fruit fruit=Basket.getFruit(appleList); //List<Apple>类型符合List<? extends Fruit>的要求
}
public void test2(){
List<Fruit> fruitList=new ArrayList<Fruit>();
Basket.addApple(fruitList, new Apple()); //List<Fruit>符合List<? super Apple>的要求
}
}
public class GenericCompileTester {
public void addInteger1(List<?> list){
list.add(1); //编译错误,因为问号代表“编译时不确定,但运行时确定”的类型,Integer不一定符合要求,其实这里相当于<? extends Object>
}
public void addInteger2(List<Object> list){
list.add(1); //编译通过,因为编译时和运行时都确定了是Object类型,Integer肯定符合要求
}
public void test(){
List<String> list=new ArrayList<String>();
addInteger2(list); //编译错误,因为List<String>不能当作List<Object>传入,否则数字1将被当作String添加,类型将不再安全
}
public void test3(){
List<? extends Fruit> fruitList=new ArrayList<Apple>();
fruitList.add(new Apple()); //编译错误,编译器无法确定List允许的是Fruit的具体哪一个子类
fruitList.add(new Fruit()); //编译错误,编译器无法确定List允许的是Fruit的具体哪一个子类
}
public void test4(){
List<? super Apple> fruitList=new ArrayList<Fruit>();
fruitList.add(new Apple()); //编译通过,Apple和Apple的子类都可以保证符合条件要求
fruitList.add(new Fruit()); //编译错误,编译器无法确定List允许的是Apple的具体那个级别的父类或接口
}
}
相关推荐
学习Java泛型能够帮助我们更好地编写和使用通用的类、接口和方法。以下是从给定文件的标题、描述、标签和部分内容中提取出的详细知识点。 1. 泛型类和泛型方法: 在Java中,泛型可以应用于类、接口和方法。泛型类和...
java泛型详细学习,深入学习java的不二之选
泛型是Java编程语言中的一个重要特性,首次在JDK 1.5版本中引入,它允许程序员在定义类、接口和方法时使用类型参数,从而实现了类型的安全性和代码的复用性。泛型的主要目标是增强类型安全,避免在运行时出现...
泛型学习笔记2的源代码,泛型 : 要解决类型的安全问题, 如果使用Object类型会带来类型的损失。典型的应用就是在集合中, 集合中理论上可以保存任意对象,实际上我们应该让它泛型化,集合类<元素类型>, 添加元素...
在Java泛型出现之前,集合框架中的类如`List`、`Map`等都是以`Object`作为类型处理元素的。这意味着在使用这些集合时,往往需要频繁地进行类型转换操作,如: ```java List list = new ArrayList(); list.add("hello...
本学习笔记主要涵盖了Java的基础知识,包括面向对象、集合、IO流、多线程、反射与动态代理以及Java 8的新特性等方面,旨在帮助初学者或有经验的开发者巩固和提升Java编程技能。 1. 面向对象(OOP):Java的核心是...
自己总结的java泛型的笔记,绘制了详细的思维导图,每个思维导图中均有详细的博文解释,方便大家学习和理解,免费分享给大家。适合java的爱好者和学习者
基于java的开发源码-java多线程反射泛型及正则表达式学习笔记和源码.zip 基于java的开发源码-java多线程反射泛型及正则表达式学习笔记和源码.zip 基于java的开发源码-java多线程反射泛型及正则表达式学习笔记和源码....
10. **泛型**:讨论Java泛型的使用,如何编写类型安全的代码,并理解其类型擦除的原理。 11. **枚举与注解**:介绍Java中的枚举类型及其用法,以及注解的定义和应用,它们在现代Java开发中有着广泛的应用。 12. **...
本资源为 Java 1.5 泛型指南中文版的详细笔记,涵盖了泛型的基础知识、泛型类、泛型方法、通配符、擦除和翻译、类型安全、类型参数、实际类型参数、擦除、翻译、转型和 instanceof、数组、Class Literals as Run-...
"Java学习笔记——良葛格"是一份专为初学者设计的教程资料,由良葛格精心编写,旨在帮助读者掌握JDK5.0版本的Java基础知识。JDK(Java Development Kit)是Java开发的核心工具集,包含了编译器、调试器和运行环境等...
此外,《JAVA学习笔记》还涵盖了集合框架,包括ArrayList、LinkedList、HashSet、HashMap等各种集合类的使用,以及泛型、迭代器和流API的应用。这些内容对于处理数据和实现复杂算法至关重要。 对于多线程编程,书中...
这个PDF学习笔记是开发者深入理解JDK 7特性和功能的重要参考资料。以下是对Java JDK 7的一些核心知识点的详细阐述: 1. **泛型改进**:在JDK 7中,泛型的使用更加灵活,引入了类型推断(Type Inference)特性,通过...
【Java基础】 Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(现已被Oracle公司收购)于1995年发布。Java以其“一次编写,到处运行”的特性...通过深入学习和实践,你可以不断提升自己的Java编程能力。
Java JDK 6学习笔记是为Java初学者量身定制的一份宝贵资料,它涵盖了Java编程的基础概念、语法以及核心特性。这份PPT简体版旨在帮助读者快速掌握Java开发的基本技能,逐步成为一名合格的Java程序员。 Java JDK...
这份《Java学习笔记》包含了丰富的知识内容,旨在帮助学习者全面掌握Java编程技术。笔记分为7个PDF部分,覆盖了从基础到高级的Java编程概念。 1. **Java基础知识**:这部分内容通常包括Java的历史背景、环境配置、...