泛型的实际类型只能是对象类型,不能是int、double等基本类型。如果泛型只在一处地方使用的话那么可以使用Object类型来替代,所以泛型适合这个类型在类或接口中多次使用的情况。
1、泛型集合
可以看到,List、Set等集合在定义的时候应该指定泛型类型(除非使用Collection来保存集合对象),即指定元素的类型,否则集合元素以Object类型来存取(泛型类不能用作instanceof操作):
Collection c = new java.util.HashSet<String>(); //使用Collection可以不指定集合元素类型
HashSet<String> hs1 = new HashSet<String>(); //指定集合元素类型为String
HashSet<String> hs2 = new HashSet<>(); //使用简化的"菱形"语法:定义部分可以不再指定元素类型
HashSet<String> hs3 = new HashSet(); //定义部分直接不用指定元素类型
if(hs1 instanceof HashSet<String>) //error! 泛型类不能用作instanceof操作
{}
HashSet<String> hs = new HashSet<>();
HashSet<Object> o = hs; //error!不同类型的集合不能赋值,即使二者类型是父子类也不行
2、泛型类
泛型类类似于上面的泛型集合,因为集合其实就是类,下面是自定义的泛型类:
package xu;
import java.util.*;
class Test<T>
{
public T info;
//private static T info; //error!泛型类的泛型不能用作static成员对象
public void SetInfo(T i){ info = i; }
public T GetInfo(){ return info; }
//public static void SetInfo(T i){ info = i; } //error!泛型类的泛型不能用作static成员方法
public static void main(String[] args)
{
Test<String> t1 = new Test<>();
Test<Integer> t2 = new Test<>();
}
}
class Foo extends Test<String> //子类需要指定父类泛型的具体类型
{
public void SetInfo(String i){ info = i; } //子类重写接口时也要指定泛型类型
}
3、泛型方法
package xu;
import java.util.*;
class Test {
public <T> void func1(T t) {
}
public <T> void func2(HashSet<T> hs) {
}
public <T> ArrayList<T> func3(){
return null;
}
public static void main(String[] args) {
Test obj = new Test();
Integer i = 100;
obj.<Integer>func1(i);
HashSet<String> hs = new HashSet<>();
obj.<String>func2(hs);
ArrayList<Integer> al2 = obj.<Integer>func3();
}
}
如果Type能被推导出来的话方法调用的时候也可以不指定具体的类型:
import java.util.*;
class Test {
public <T> void func1(T t) {
}
public <T> void func2(HashSet<T> hs) {
}
public <T> ArrayList<T> func3(){
return null;
}
public static void main(String[] args) {
Test obj = new Test();
Integer i = 100;
obj.<Integer>func1(i);
obj.func1(i); //根据参数自动推导出泛型类型为String,不用在方法调用的时候指定泛型类型
HashSet<String> hs = new HashSet<>();
obj.<String>func2(hs);
obj.func2(hs); //根据参数自动推导出泛型类型为String,不用在方法调用的时候指定泛型类型
ArrayList<Integer> al2 = obj.<Integer>func3();
ArrayList<Integer> al = obj.func3(); //通过方法返回值的赋值目标类型自动推导出方法的泛型类型为String,不用在方法调用的时候指定泛型类型
}
}
4、泛型构造函数
package xu;
import java.util.*;
class Test
{
public <T> Test(T t){}
public static void main(String[] args)
{
Test t2 = new<Integer> Test(10);
}
}
class Test<T>
{
public<E> Test(E e){}
public static void main(String[] args)
{
Test<Integer> t2 = new<String/*构造方法的泛型类型*/> Test<Integer/*类的泛型类型*/>("test");
}
}
如果Type能被推导出来的话对象定义的时候也可以不指定具体的类型:
class Test
{
public <T> Test(T t) {}
public static void main(String[] args)
{
// Test t1 = new<String> Test("test");
Test t1 = new Test("test"); //根据参数自动推导出构造器中泛型类型为String,不用在定义的时候指定泛型类型
}
}
class Test<T>
{
public<E> Test(E e){}
public static void main(String[] args)
{
//Test<Integer> t1 = new<String> Test<Integer>("test");
Test<Integer> t1 = new Test<>("test"); //根据参数自动推导出构造器中泛型类型为String,不用在定义的时候指定泛型类型
}
}
5、泛型接口
上面为java.util.function包下的函数式接口Consumer和Function,下面为一个自定义的泛型接口:
interface Comparator<T>{
int compare(T o1, T o2);
}
class StringCompartor implements Comparator<String>{ //实现该接口的时候需要指定泛型类型
@Override
public int compare(String s1, String s2){
return -s1.compareTo(s2);
}
}
public class Test {
public void func1(Comparator<Integer> com) {} //定义该接口的对象的时候需要指定泛型类型
public void func2(Comparator<?> com) {} //当泛型对象为函数参数的时候也可以使用通配符来指定泛型类型
}
6、extends、super与通配符?
T extends Foo表示泛型类型必须是Foo类或Foo的子类,T extends Iterable表示泛型类型必须操作了Iterable接口,T extends Foo & Iterable表示T类型必须是Foo类或其子类且操作了Iterable接口,T extends Iterable & Comparable表示T类型必须实现了Iterable和Comparable接口。
class Foo{}
class FooChild extends Foo{}
class Bar<T extends Foo> {}
public class Main {
public static void main(String[] args)throws Exception{
Bar<FooChild> b = new Bar<>();
}
}
我们在定义泛型类对象的时候需要指定泛型的类型,也可以指定泛型的类型为某一个类型或其父类(使用? extends className),也可以指定泛型的类型为某一个类型或其子类(使用? super className),也可以指定泛型类型为所有类型(使用?,表示通配符,不限类型):
class Bar<T> {
public T value;
}
class Parent{}
class Child extends Parent{}
public class Main {
public static void main(String[] args)throws Exception{
Bar<? extends Parent> b1; //b1可接受泛型类型为Parent或其子类类型的对象
Bar<? super Child> b2; //b2可接受泛型类型为Child或其父类类型的对象
Bar<?> b3; //b3可接受泛型类型不限制的对象,相当于是Bar<? extends Object>
Bar<Child> bIns1 = new Bar<>();
b1 = bIns1;
Bar<Parent> bIns2 = new Bar<>();
b2 = bIns2;
Bar<Integer> bIns3 = new Bar<>();
b3 = bIns3;
}
}
extends、super与通配符?一般经常用在声明函数的参数的时候:
class Fruit{}
class Apple extends Fruit{}
class Bar<T> {
public T value;
}
public class Main {
public static void func1(Bar<Integer> b){}; //传入的Bar对象的泛型类型只能是Integer
public static void func2(Bar<?> b) {} //使用类型通配符?,传入的Bar对象的泛型类型可以是任何类型
public static void func3(HashSet<?> hs){} //使用类型通配符?,传入的HashSet对象的元素类型可以是任何类型
public static void fun4(List<? extends Foo> l){} //传入的List的元素类型应该是Foo类型或其Foo类的子类类型
public static void fun5(Foo<? super Bar> f){} //传入的Foo对象的泛型类型应该是Bar或Bar的父类类型
public static void main(String[] args)throws Exception{
Bar<Apple> f1 = new Bar<>();
Bar<Fruit> f2 = f1; //错误
Bar<Fruit> f3 = new Bar<>();
Bar<? super Apple> f4 = f3;
}
}
当函数的返回值使用通配符的话,那么也应该使用通配符来保存这个返回值,如下所示,即我们可以将符合泛型条件的对象赋值给使用通配符的对象,但不能将使用通配符的对象赋值给带具体泛型的对象:
class Bar<T>
{
public static void test()
{
System.out.println("test func()");
}
}
public class Main {
public static void main(String[] args)throws Exception
{
Bar<Integer> b1 = func1(); // 错误!
Bar<?> b2 = func1();
b2.test();
Bar<Integer> b3 = (Bar<Integer>)func1();
b3.test();
}
public static Bar<?> func1() { return null; }
}
还可以指定泛型类型是另一种泛型类型或其子类或父类,比如下为java中StackWalker类的一个方法walk(),这个方法参数为一个Function,Function的第二个泛型类型指定为了另一个泛型类型T或者其子类类型,而且在调用walk()方法的时候不用指定泛型类型T,因为可以从walk()方法源码的返回值类型中推导出来:
StackWalker stackWalker = StackWalker.getInstance();
//Optional<StackWalker.StackFrame> frame = stackWalker.<Optional<StackWalker.StackFrame>>walk(frameSream->frameSream.findFirst());
Optional<StackWalker.StackFrame> frame = stackWalker.walk(frameSream->frameSream.findFirst());
以下两个方法的参数输入实际是一样的,都表示src元素类型应该是dest元素类型的子类或与之相同:
public <E> void copy1(Collection<E> dest, Collection<? extends E> src);
public <E> void copy2(Collection<? super T> dest, Collection<T> src);
很多时候都可以使用泛型方法来替换类型通配符,如下所示的Collection接口中的两个方法,但因为实际上T只在方法中有一处使用,所以使用类型通配符更加方便。
interface Collection<E>
{
//使用类型通配符
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
//使用泛型方法
//<T> boolean containsAll(Collection<T> c);
//<T extends E> boolean addAll(Collection<T> c);
//...
}
7、其它
Java 8改进的类型推断:
class MyUtil<E>
{
public static <Z> MyUtil<Z> nil()
{
return null;
}
public static <Z> MyUtil<Z> cons(Z head, MyUtil<Z> tail)
{
return null;
}
}
public class InferenceTest
{
public static void main(String[] args)
{
// 可以通过方法赋值的目标参数来推断类型参数为String
MyUtil<String> ls = MyUtil.nil(); //等价于:MyUtil<String> mu = MyUtil.<String>nil();
// 可调用cons方法所需的参数类型来推断类型参数为Integer
MyUtil.cons(42, MyUtil.nil()); //等价于: MyUtil.cons(42, MyUtil.<Integer>nil());
}
}
将一个具有泛型信息的对象赋给没有泛型信息的对象后泛型信息会被擦除(成为Object):
List<String> li = new ArrayList<>(); //li具有泛型信息
li.add("test");
List l = li; //l没有泛型信息
String s2 = l.get(0); //编译错误,l中元素类型为Object
可以创建泛型数组:
List<String>[] ls = new ArrayList[10]; //创建泛型数组(只在数组声明的时候指定泛型类型,定义的时候不能指定)
List<?>[] lsa = new ArrayList<?>[10]; //创建通配符泛型数组(数组声明和定义的时候都指定通配类型)