Java语法糖(3):泛型

泛型初探

在泛型(Generic type或Generics)出现以前,是这么写代码的:java

 

public static void main(String[] args)
{
 List list = new ArrayList();
 list.add("123");
 list.add("456");
 
 System.out.println((String)list.get(0));
}

固然这是彻底容许的,由于List里面的内容是Object类型的,天然任何对象类型均可以放入、均可以取出,可是这么写会有两个问题:sql

一、当一个对象放入集合时,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型变成了Object安全

二、运行时须要人为地强制转换类型到具体目标,实际的程序毫不会这么简单,一个不当心就会出现java.lang.ClassCastException,即类型转换异常架构

因此,泛型出现以后,上面的代码就改为了你们都熟知的写法:并发

 

public static void main(String[] args)
{
 List<String> list = new ArrayList<String>();
 list.add("123");
 list.add("456");
 
 System.out.println(list.get(0));
}

这就是泛型。泛型是对Java语言类型系统的一种扩展,有点相似于C++的模板,能够把类型参数看做是使用参数化类型时指定的类型的一个占位符。引入泛型,是对Java语言一个较大的功能加强,带来了不少的好处:分布式

一、类型安全。类型错误如今在编译期间就被捕获到了,而不是在运行时看成java.lang.ClassCastException展现出来,将类型检查从运行时挪到编译时有助于开发者更容易找到错误,并提升程序的可靠性高并发

二、消除了代码中许多的强制类型转换,加强了代码的可读性性能

三、为较大的优化带来了可能学习

getClass()相同优化

看一段代码:

 

public static void main(String[] args)
{
 List<String> stringList = new ArrayList<String>();
 List<Integer> integerList = new ArrayList<Integer>();
 System.out.println(stringList.getClass() == integerList.getClass());
}

运行结果为:

 

true

这意味着,泛型是什么并不会对一个对象实例是什么类型的形成影响,因此,经过改变泛型的方式试图定义不一样的重载方法也是不能够的:

Java语法糖(3):泛型

 

尽可能使用精确的类型定义泛型

尽可能使用精确的类型定义泛型,除非必要,不然不要写一个接口或者父类上去:

 

public static void main(String[] args)
{
 List<Number> list = new ArrayList<Number>();
 list.add(4);
 list.add(2.2);
 for (Number number : list)
 System.out.println(number);
}

就像这样,list中的是一个Number类型,往里面添加的是Integer与Double,这样致使get出来的元素也都是Number类型的,失去了子类扩展的功能。若是要让子类变为Interger和Double也能够,(Integer)list.get(0)和(Double)list.get(1)强转就能够了,可是这样不就失去了泛型的意义了吗?因此,尽可能用精确的类型去定义泛型。

使用类型通配符

List<Object>不是List<String>的父类型,List<Integer>不是List<Number>的父类型,试图用如下方式赋值是不容许的:

 

public static void main(String[] args)
{
 List<Number> numberList = new ArrayList<Number>();
 List<Integer> integerList = new ArrayList<Integer>();
 numberList = integerList;
}

第5行将报错”Type mismatch: cannot convert from List<Integer> to List<Number>”。有人可能以为这样很不方便:我在一个方法里面只须要循环检索一个List,也不能利用多态放一个父类型进去,也不能重载,那怎么办呢?针对这个问题,Java给开发者提供了通配符”?”,看一下:

 

public static void main(String[] args)
{
 List<String> stringList = new ArrayList<String>();
 List<Integer> integerList = new ArrayList<Integer>();
 
 printList(stringList);
 printList(integerList);
}
 
private static void printList(List<?> l)
{
 for (Object o : l)
 System.out.println(o);
}

<?>是类型通配符,表示是任何泛型的父类型,这样List<Object>、List<String>这些均可以传递进入printList方法中,注意这里的参数不能写成List<E>,这样就报错了,E未定义。固然<?>也能够不加,不过这样会有警告:若是传递一个List<E>给List,至关于传递一个只承诺将它看成List(原始类型)的方法,这将会破坏使用泛型的类型安全

再注意一点,使用类型通配符,只能从中检索元素,不能添加元素

泛型方法

 

public static void main(String[] args)
{
 System.out.println(ifThenElse(false, "111", "222"));
}
 
private static <T> T ifThenElse(boolean b, T first, T second)
{
 return b ? first : second;
}

返回结果为:

 

222

这说明,方法也能够被泛型化,无论定义在其中的类是否是泛型化的。这意味着不用显式告诉编译器,想要T什么值:编译器只知道这些T都必须相同。

静态资源不认识泛型

接上一个话题,若是把<T>去掉,那么:

Java语法糖(3):泛型

 

报错,T未定义。可是若是咱们再把static去掉:

Java语法糖(3):泛型

 

这并不会有任何问题。两相对比下,能够看出static方法并不认识泛型,因此咱们要加上一个<T>,告诉static方法,后面的T是一个泛型。既然static方法不认识泛型,那咱们看一下static变量是否定识泛型:

Java语法糖(3):泛型

 

这证实了,static变量也不认识泛型,其实不只仅是static方法、static变量,static块也不认识泛型,能够本身试一下。总结起来就是一句话:静态资源不认识泛型

泛型约束

能够对泛型参数做约束,原本以为应该有规律,后来发现没有,那就把本身研究的结论发一下,假设有一组类继承关系C继承自B,B继承自A:

一、定义class的时候只能使用extends关键字且不能用通配符”?”

 

public class TestMain<T extends B>
{
 public static void main(String[] args)
 {
 new TestMain<C>();
 }
}

正确。TestMain类的泛型只能传B的子类,也就是C。”new TestMain<A>()”、”public class TestMain<? extends B>”、”public class TestMain<T super B>”都是错误的写法

二、做为方法的参数,泛型可使用”? extends B”或者”? super B”,前者表示实际类型只能够是B的子类,后者表示实际类型只能够是B的父类,如下两种写法都是正确的:

 

public static void main(String[] args)
{
 print(new ArrayList<C>());
}
 
public static void print(List<? extends B> list)
{
 
}

public static void main(String[] args)
{
 print(new ArrayList<A>());
 print(new ArrayList<Object>());
}
 
public static void print(List<? super B> list)
{
 
}

三、做为局部变量的参数,泛型可使用”? extends B”或者”? super B”,不过前者好像没什么意义,后者表示只能够传以B为父类的对象,因此如下的写法是正确的:

 

public static void main(String[] args)
{
 List<? super B> list = new ArrayList<B>();
 list.add(new C());
}

不要写”list.add(new A())”,JDK将会认为这是类型不匹配的。

欢迎工做一到五年的Java工程师朋友们加入Java架构开发 : 867748702 群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、 Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper, Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料) 合理利用本身每一分每一秒的时间来学习提高本身, 不要再用"没有时间“来掩饰本身思想上的懒惰!趁年轻,使劲拼,给将来的本身一个交代!

相关文章
相关标签/搜索