泛型是一种参数化类型的机制。它可使得代码适用于各类类型,从而编写更加通用的代码,例如集合框架。java
泛型是一种编译时类型确认机制。它提供了编译期的类型安全,程序员
1,类型安全。 泛型的主要目标是提升 Java 程序的类型安全。经过知道使用泛型定义的变量的类型限制,编译器能够在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者若是幸运的话,还存在于代码注释中)。数组
2,消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,而且减小了出错机会。安全
3,潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。可是更多类型信息可用于编译器这一事实,为将来版本的 JVM 的优化带来可能。因为泛型的实现方式,支持泛型(几乎)不须要 JVM 或类文件更改。全部工做都在编译器中完成,编译器生成相似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。框架
定义泛型类 在 类名后面 加上<T> 表示这是个泛型类ide
public class Crate<T> { private T contents; public T emptyCrate() { return contents; } public void packCrate(T contents) { this.contents = contents; } }
这个泛型类型,能够在类内任何地方出现,函数
如 属性类型,方法的返回值,方法的参数类型。性能
在生成实例的时候 必须指定具体类型。优化
// create an instance with generic type Crate<Elephant> crateForElephant = new Crate<>();
泛型数量能够是多个this
public class SizeLimitedCrate<T, U> { private T contents; private U sizeLimit; public SizeLimitedCrate(T contents, U sizeLimit) { this.contents = contents; this.sizeLimit = sizeLimit; } } // create an instance with generic types SizeLimitedCrate<Elephant, Integer> c1 = new SizeLimitedCrate<>()
泛型类命名规范
理论上来讲,泛型的类型名字能够定义成任何你想要的。为了方便起见,提升可读性,JDK建议你们采用 单个大写字母,区分泛型与真实类名,同时提供了一些经常使用的建议泛型
E 表示一个元素
K 表示一个键值对的键
V 表示一个键值对的值
N 表示一个数字
T 表示一个通用类型
若是是多个通用类型,能够延续使用,S, U, V, .
泛型类支持接口定义, 即定义一个泛型接口。
public interface Shippable<T> { void ship(T t); }
那么问题来了,这个泛型类怎么去实现?有三种方式能够实现
在实现接口的同时 指定具体的类型 而不用泛型表示。
class ShippableRobotCrate implements Shippable<Robot> { public void ship(Robot t) { } }
在实现接口的同时,本身也变成泛化类,进一步,泛化下沉。
class ShippableAbstractCrate<U> implements Shippable<U> { public void ship(U t) { } }
在实现接口的同时,不继续使用泛型,取而代之的是Object类型,这是个古老方法,主要是为了向前兼容,对那些没有泛型支持的兼容。
对于编译器而言,会抛出警告,可是会经过编译。
class ShippableCrate implements Shippable { public void ship(Object t) { } }
1. 构造函数不能泛型, 如new T() 最终变成 new Object()
2. 不能使用静态类型的数组
3. 不能使用instanceof, 运行的时候泛型会被擦除
4. 不能使用基本类型做为泛型的参数,能够经过封装类如:Integer
5. 不能使用静态类型做为参数
泛型方法 与 泛型类有点相似,只是它做用与具体的方法,范围相对于泛型类 更小。
在定义方法的时候 在声明返回值的前面 使用<T> 来声明泛型方法。
public static <T> void sink(T t) { }
对于方法的返回类型,也能够是泛型或者是泛型类。
// 返回一个泛型 public static <T> T identity(T t) { return t; } // 返回一个泛型类 public static <T> Crate<T> ship(T t) { System.out.println("Preparing " + t); return new Crate<T>(); }
一样的, 泛型方法支持多个 泛型类型
// 返回一个泛型 public static <T,U> T identity(T t,U u) { return t; }
调用具体的泛型方法时,须要指定具体类型
Box.<String>ship("package"); Box.<String[]>ship(args);
调用泛型方法的时候能够向正常的方法调用同样, java编译器会自动匹配泛型
Box.ship("package");
泛型的出现帮助编译器可以在编译的时候,使用正确的类型。
实际上,编译器 是将全部的泛型替换为 Object,换句话说,代码编译以后,这些泛型都将被Object所取代。这么作的目的主要是为了兼容老版本的代码(非泛型)
public class Crate { private Object contents; public Object emptyCrate() { return contents; } public void packCrate(Object contents) { this.contents = contents; } }
也不用过于担忧 这个泛型擦除,编译期会自动转型了那些被擦除了泛型 如:
当你调用方法: Robot r = crate.emptyCrate();
编译期 实际会编译出显示转型的代码
Robot r = (Robot) crate.emptyCrate();
class Dragon {} class Unicorn { } public class LegacyDragons { public static void main(String[] args) { List unicorns = new ArrayList(); unicorns.add(new Unicorn()); printDragons(unicorns); } private static void printDragons(List<Dragon> dragons) { for (Dragon dragon: dragons) { // ClassCastException System.out.println(dragon); } } }
虽然有了泛型擦除,但java 毕竟是动态强类型语言,在实际使用过程当中,与老代码结合的使用也会出现问题。
泛型的通配表示的是为知类型,经过? 表示
对一个泛型的通配有三种方式来使用它
类型 | 语法 | Example |
无界通配 | ? | List<?> l =new ArrayList<String>(); |
上界通配 |
? extends type | List<? extends Exception> l =new ArrayList<RuntimeException> (); |
下界通配 |
? super type | List<? super Exception> l =new ArrayList<Object>(); |
java 是强类型语言, 因此,对于
List<Object> keywords = new ArrayList<String>();
是不能经过编译的, 若是使用了通配就可。
public static void printList(List<?> list) { for (Object x: list) System.out.println(x); } public static void main(String[] args) { List<String> keywords = new ArrayList<>(); keywords.add("java"); printList(keywords); }
假如 咱们要设定一个继承关系的泛型
ArrayList<Number> list = new ArrayList<Integer>(); // DOES NOT COMPILE
List<? extends Number> list = new ArrayList<Integer>(); // compiled
上界通配表示 任何一个 Number的子类包括它本身均可以被匹配进来
public static long total(List<? extends Number> list) { long count = 0; for (Number number: list) count += number.longValue(); return count; } // 有了上界的泛型,在基于泛型擦除的机制,会将Object 强转成泛型上界 public static long total(List list) { long count = 0; for (Object obj: list) { Number number = (Number) obj; count += number.longValue(); } return count; }
须要注意的是,使用了上界通配的列表 是不能添加元素,从java的角度来看,编译期并不知道
添加的元素的具体是哪个,由于任何extends type均可能。
static class Sparrow extends Bird { } static class Bird { } public static void main(String[] args) { List<? extends Bird> birds = new ArrayList<Bird>(); birds.add(new Sparrow()); // DOES NOT COMPILE birds.add(new Bird()); // DOES NOT COMPILE }
与上界通配相似,表示 任何一个 超类包括它本身均可以被匹配进来
public static void addSound(List<? super String> list) { // lower bound list.add("quack"); }
在使用泛型的时候能够遵循一些基本的原则,从而避免一些常见的问题。