什么是泛型
什么是泛型?
泛型是 Java SE5 出现的新特性,泛型的本质是类型参数化或参数化类型,在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。
泛型类,接口
1.泛型类
泛型类的定义格式:
-
格式:修饰符 class 类名<类型>{}
-
泛例:public class Generic {}
-
T可以是任意标识
public class Generic<T>{ private T t; public T get(){ return t; } public void set(T t){ this.t = t; } }
public class CollectionDemo {
public static void main(String[] args) {
Generic<String> stu = new Generic<String>();
stu.set("鸣人");
System.out.println(stu.get());
Generic<Integer> stu1 = new Generic<Integer>();
stu1.set(10);
System.out.println(stu1.get());
}
}
2.泛型接口
泛型接口的定义格式:
-
格式:修饰符 interface 接口名<类型>{}
-
范例:public interface Generic {}
public interface GenInterface<T> { void show(T t); }
public class GenImpl<T> implements GenInterface<T>{ @Override public void show(T t) { System.out.println(t); } }
public class CollectionDemo{ public static void main(String[] args) { GenImpl<String> str = new GenImpl<String>(); str.show("加油"); GenImpl<Integer> i = new GenImpl<>(); i.show(30); } }
泛型方法
泛型方法的定义格式:
- 格式:修饰符 <类型> 返回值类型方法名(类型变量名){}
- 范例:public void show(T t){}
public <T> T show(T t){
return t;
}
public <T> void show1(T t){
System.out.println(t);
}
类型通配符和泛型上下限
为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符:<?>
- List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
- 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果不希望List<?>是任何泛型的父类,只希望代表某一类泛型List的父类,可以使用类型通配符的上限。
类型通配符上限:<?extends类型>
List<? extends Number>:表示的类型是Number或者其子类型
除了可以指定类型通配符的上限,也可以指定类型通配符的下限:
类型通配符下限:<?super类型>
List<?super Number>:表示的类型是Number或者其父类型
import java.util.ArrayList;
import java.util.List;
public class CollectionDemo{
public static void main(String[] args) {
List<?> numbersList = new ArrayList<Number>();
//类型通配符上限
List<? extends Number> list1 = new ArrayList<Number>();
List<? extends Number> list2 = new ArrayList<Integer>();
//类型通配符下限
List<? super Number> list3 = new ArrayList<Number>();
}
}
<?> 无限制通配符
<? extends E> extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
<? super E> super 关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
// 使用原则《Effictive Java》
// 为了获得最大限度的灵活性,要在表示 生产者或者消费者 的输入参数上使用通配符,使用的规则就是:生产者有上限、消费者有下限
1. 如果参数化类型表示一个 T 的生产者,使用 < ? extends T>;
2. 如果它表示一个 T 的消费者,就使用 < ? super T>;
3. 如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。
泛型擦除
Java语言的泛型实现方式是擦拭法(Type Erasure)。
所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型。
Java 的泛型是伪泛型,因为在编译期间所有的泛型信息都会被擦除掉,泛型参数保留为原始类型。譬如 List 在运行时仅用一个 List 来表示(所以我们可以通过反射 add 方法来向 Integer 的泛型列表添加字符串,因为编译后都成了 Object),这样做的目的是为了和 Java 1.5 之前版本进行兼容
————————————————
原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。
如果类型变量有限定,那么原始类型就用第一个边界的类型变量类替换。
比如: Pair这样声明的话
public class Pair {}
那么原始类型就是Comparable。
Java编译器是通过先检查代码中泛型的类型,然后再进行类型擦除,再进行编译
分类:
泛型与数组
首先,我们泛型数组相关的申明:
List<String>[] list11 = new ArrayList<String>[10]; //编译错误,非法创建
List<String>[] list12 = new ArrayList<?>[10]; //编译错误,需要强转类型
List<String>[] list13 = (List<String>[]) new ArrayList<?>[10]; //OK,但是会有警告
List<?>[] list14 = new ArrayList<String>[10]; //编译错误,非法创建
List<?>[] list15 = new ArrayList<?>[10]; //OK
List<String>[] list6 = new ArrayList[10]; //OK,但是会有警告
public class GenericsDemo30{
public static void main(String args[]){
Integer i[] = fun1(1,2,3,4,5,6) ; // 返回泛型数组
fun2(i) ;
}
public static <T> T[] fun1(T...arg){ // 接收可变参数
return arg ; // 返回泛型数组
}
public static <T> void fun2(T param[]){ // 输出
System.out.print("接收泛型数组:") ;
for(T t:param){
System.out.print(t + "、") ;
}
}
}
合理使用
public ArrayWithTypeToken(Class<T> type, int size) {
array = (T[]) Array.newInstance(type, size);
}
泛型与反射
反射和泛型都是Java的一种动态技术。而不像继承和多态,是面向对象的技术
我们知道,使用反射的一个最大的烦恼就是应用反射以后得到的基本上都是Object类型的对象,这种对象要使用,需要我们进行强制类型转化 而我们更知道,泛型的功能之一就是消除我们使用强制类型转化
1.反射常用的泛型类
Class
Constructor
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 泛型与反射
*/
public class Test11 {
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
Constructor<Person> constructor = personClass.getConstructor();
Person person = constructor.newInstance();
}
}