泛型入门
在JDK1.5以前是没有泛型的,那么我们通常是怎么做的呢?
public class LessJDK5Demo {
public static void main(String[] args) {
List list=new ArrayList();
User user=new User(1,"zhangsan");
list.add(user);
User u=(User)list.get(0);
System.out.println(u);
}
}
可以看得出来,每次从list里取数据的时候,需要强制转换,所以这里就很容易报异常:ClassCastException.
1
编译时不检查类型的异常
public static void main(String[] args) {
List list=new ArrayList();
list.add("Java后端技术栈");
list.add(1);
System.out.println((String) list.get(1));
}
运行:
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.lang.String
at com.lawt.swaggerdemo.generic.LessJDK5Demo.main(LessJDK5Demo.java:18)
2
使用泛型
JDK1.5以后引入了“参数化类型”的概念,运行程序在创建集合的时候指定集合元素的类型,比如:List<String> 说明这个list只能存放字符类型的对象,Java的参数化类型被称为泛型。
public class LessJDK5Demo {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("Java后端技术栈");
list.add(1);
list.forEach(str-> System.out.println(str.length()));
}
}
这样就保证了不会出现类型强转异常。
3
增强的菱形语法
在JDK1.7以前,如果使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器的后面必须带泛型,这样有点闲的多余,比如以下两个使用案例:
List<String> strings=new ArrayList<String>();
Map<String ,Integer> map=new HashMap<String ,Integer>(2);
从JDK1.7开始,上面的两个使用案例可以改写成:
//Java自动推断出ArrayList的<>里应该是String
List<String> strings=new ArrayList<>();
Map<String ,Integer> map=new HashMap<>(2);
以上把两个尖括号并排放在一起非常想菱形,所以这里也就被称之为“菱形”语法。
JDK1.8对菱形语法做了增加,它甚至允许在创建匿名内部类的时候使用菱形语法,Java课根据上下问推断匿名内部类中泛型的类型,比如:
interface Foo<T>{
void test(T t);
}
public class Test {
public static void main(String[] args) {
Foo<String> f=new Foo<String>() {
@Override
public void test(String s) {
System.out.println(s);
}
};
Foo<?> foo1=new Foo<Object>() {
@Override
public void test(Object o) {
System.out.println(o);
}
};
Foo<? extends Number> fn=new Foo<Number>() {
@Override
public void test(Number number) {
System.out.println(number);
}
};
}
}
深入泛型
所谓泛型:就是允许在定义类、接口、方法时使用类型参数,这个类型参数(泛型)将在声明变量、创建对象、调用方法时动态指定(即时传入实际的类型参数)。
1
定义泛型接口、类
以下是JDK1.5版本改写后List/Set/Map的代码片段:
//定义接口时指定了一个泛型形参,该形参为E
public interface List<E> {
//在该接口里,E可作为类型使用
Iterator<E> iterator();
<T> T[] toArray(T[] a);
//...
}
public interface Set<E> {
boolean add(E e);
//.....
}
public interface Map<K,V> {
V put(K key, V value);
//....
}
上面三个接口声明是比较简单的,除了尖括号中内容---这就是泛型的实质:允许在定义接口、类时声明泛型形参,泛型形参在整个接口、类体重可当成类型使用,几乎所有可使用普通类型的地方都可以使用这种泛型参型。
比如:前面的List类型时,,如果E形参传入String类型实参,则可以理解为产生了一个新类:List<String>类型,或者可以想象成生成了一个逻辑上的子类。比如:
public interface ListString extends List{
void add(String str);
//.....
}
这里只是逻辑上子类,并不是物理上的。
注意
包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态低生成多个逻辑上的子类,但是这种子类在物理上并不存在。
2
从泛型派生子类
public class Person<T> {
private T info;
public Person(T info) {
this.info = info;
}
//get set ....
}
第一种方式:原始类型
public class Sub extends Person {
public Sub(Object info) {
super(info);
}
}
就是说不为其指定类型也是可以的。
第二种:指定泛型参数
public class Doc extends Person<String> {
public Doc(String info) {
super(info);
}
}
以上是指定泛型类型为String类型。
3
并不存在泛型类
前面说过List<String>,可以理解为List生成了一个子类List<String>,事实上也很像一个特殊List类,该List只能添加Sting的集合。但是实际上系统根本没有生成什么子类class文件,而且也不把List<String>当做一个新类来处理。为什么呢?下面的代码就是很好解释:
List<String> strings=new ArrayList<>();
List<Integer> integers=new ArrayList<>();
System.out.println(strings.getClass()==integers.getClass());
如果真的生成了一个新class类,那么,上面应该输出false,但是实际上输出的是true。
全部写完篇幅会很长,所以留到《再谈Java泛型---下》中继续扯;
既然在看了,就点一下吧!!