public class Holder {
//泛型定义在类上,需要写在类名的后面,并用加以显示
public T a;
public Holder() {
//需要注意的是:构造函数是不加任何泛型<>的
}
public Holder(T a) {
this.a = a;
}
public void set(T a) {
this.a = a;
}
public T get() {return a;}
public static void main(String[] args) {
Holder holder = new Holder<>();
//JDK7以后,右侧就不必再次申请泛型了,叫做“砖石语法”
holder.set(new Automobile());
Automoblie auto = holder.get();
}
}
// onjava/Tuple2.java
package onjava;
public class Tuple2 {
public final A a1;
public final B a2;
public Tuple2(A a, B b) { a1 = a; a2 = b; }
public String rep() { return a1 + ", " + a2; }
@Override
public String toString() {
return "(" + rep() + ")";
}
}
public class StaticGenerator() {
//public static void show(T t) //{
//这样定义会报错,使用泛型的静态方法需要额外的泛型声明,如下
//}
public static void show(T t) {
//Right
}
}
public class GenericMethods {
public T method(T t) {
//代表这是一个泛型方法,该方法的入参需要泛型T,而返回值T代表这个方法的返回值类型是T
}
public T get() {
//该方法没有,表明该方法不是泛型方法,也就是意味着该方法的参数不需要泛型,但是该方法可以返回T类型的数据
}
public void method1(T t, K v) {
//同理,表明这个方法是一个泛型方法,其入参需要两种类型的参数,返回值的void
}
}
public class GenericVarargs {
@SafeVarargs
public static List makeList(T... args) {
List result = new ArrayList<>();
for (T item : args)
result.add(item);
return result;
}
}
@SafeVarargs 注解保证我们不会对变长参数列表进行任何修改。
public class ArrayAlg {
public static T min(T[] a) {
T smallest = a[0];
for(int i = 1; i < a.length; i++) {
if (smallest.compareTo(a[i]) > 0) smallest = a[i];
return smallest;
}
}
}
由于smallest的类型是T,表明T可以是任意类型的,但是为了确保T类型拥有compareTo()方法,因此,我们必须对T类型进行一定的限定.
那就是只有实现了Comparable接口的类才可以,因此,该泛型方法应该写成:
public static T min(T[] a) {
.....
}
T extends Comparable & Serializable
public void f(T t, U u) {
...
}
@Test
public void test1() {
List stringList = new ArrayList<>();
List integerList = new ArrayList<>();
System.out.println(stringList.getClass() == integerList.getClass());
System.out.println(stringList.getClass());
}
输出:
true
class java.util.ArrayList
public class Pair {
private T first;
}
================>
//被擦除后为:
public class Pair {
private Object first;
}
public class Interval {
private T lower;
private T upper;
}
=========>
//则被替换为:
public class Interval {
private Comparable lower;
private Coparable upper;
}
当然如果把泛型限定类型写为:
T extends Serializable & Comparable,
那么泛型擦除后就是:
private Serializable;
但是Serializable只是一个标签接口,里面不含有任何方法,因此这种接口应该放在泛型限定类型列表的最后。
泛型并不是Java独有的特色,但是Java为了兼容以前的版本,对泛型做了一些限制。
不能用类型参数代替基本类型,就是泛型中的T,不能使用基本类型来替换。
如:只有Pair而没有Pair
所谓的类型查询,就是调用instanceof或者调用getClass()这样的方法时,会对instanceof后面和getClass()前面的进行类型检查。而原始类型是指不带泛型时候的类型。
@Test
public void test2() {
GenertorTest a = new GenertorTest<>();
//因为GenertorTest在运行期间会被泛型擦除为Genertor,
//因此,使用如下两个带有泛型的判定是无法通过编译的
System.out.println(a instanceof GenertorTest);
System.out.println(a instanceof GenertorTest);
//这样的判定输出:true
System.out.println(a instanceof GenertorTest);
}
例如,不能创建如下数组:
Pair[] table = new Pair[10]
如果有必须,可以使用如下方式:
ArrayList:ArrayList>
不能使用像new T(…), new T[…]或者T.class这样的表达式中的类型变量,如下,是无法通过编译的:
public class GenertorTest {
private T t;
public GenertorTest() {
//无法编译
t = new T();
}
}
因为T没有给点限定的情况下,T是要被Object取代的,而我们又不想让其调用new Object()。
public GenertorTest(T t) {
this.t = t;
}
public static GenertorTest makeGenetorTest(Supplier constr) {
return new GenertorTest<>(constr.get());
}
调用时:
@Test
public void test3() {
GenertorTest genertorTest1 = GenertorTest.makeGenetorTest(String::new);
}
静态成员变量和静态方法的返回值不能是泛型T,如下,是无法通过编译的:
private static T a; //无法编译
private static T a() {
//无法编译
}
//不能混为一谈的是,静态方法可以是泛型方法,但是返回值不能是泛型,可以是其他或者是包含了泛型的类
public static void b() {
//Right
}
public static List c() {
//Right
}
既不能抛出也不能捕获泛型类对象,甚至泛型类无法继承Expection或实现Throwable。
如下,是无法编译的:
public class GenertorTest extends Exception{
//无法编译
}
public class GenertorTest extends Exception {
//无法编译
}
不能抛出泛型类对象,因为抛出异常通常都是new XXException(),而new T()本身就是不支持的,
而捕获异常如:
catch(T e) //无法编译
也是无法编译的。
但是,使用类型变量是合法的:
public static
因为类型变量T会被擦除为Throwable.
1、 无论S和T有什么联系(S和T无联系,S继承T,T继承S等),通常Pair和Pair都没有什么联系;
? extends Employee
的set和get看成:
? extends Employee get()
void set(? extends Employee)
get()的时候必然会获得一个Employee或其子类,但是set()的时候编译器无法知道存入的元素是否是Employee的子类或其本身,因此? extends Emplooy是无法调用set()方法的;
? super Employee
的set和get可以看成:
? super Employee get()
void set(? super Empployee)
get()的时候会获得Employee或其超类,因此无法准确获得其类型,故只能将它赋值给一个Object;
但是set()的时候可以set进入Employee或其子类,因为?是Employee的基类,也必定是Employee子类的基类;
总结:带有超类限定的通配符可以向泛型对象写入;带有子类限定的通配符可以从泛型对象读取。
3. 值得注意的是:**通配符不是类型变量,因此,不能在编写代码中使用"?"作为一种类型
因此,如下方法不是泛型方法:
public static void swap(Pair> p)
Pair>,无限定通配符对应的set和get方法如下:
? get()
void set(?)
getFirst返回值只能赋给一个Object,而set方法是不能够被调用的,甚至不能用Object调用;但是可以调用:
set(null)
?不能作为类型变量,但是T可以明确指定一种类型,因此我们可以使用T来捕获?
//通配符方法
public static void swap(Piar> p) {
? t = p.getFirst();
p.setFirst(p.getSecond);
p.setSecond(t);
}
//使用通配符捕获方法
public static void swapHelper(Pair p) {
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
//进而改进为:
public static void swap(Pair> p) {
swapHelper(p);
}
public class Plate { //定义了一个泛型类
//定义了一个泛型方法
public void method() {
}
}
//定义一个普通类
class Fruit {}
//定义一个实现类
class Apple extends Fruit {}
//?此时通配符就可以用在实例引用上了(此时是有问题的)
Plate plate = new Plate();
Plate extends Fruit> palte = new Plate();
此时Plate extends Fruit>是Plate和Plate的基类,但是虽然是基类,但是JVM仍旧无法预知?是什么类型,因此上界通配符,适用于get(),但不能set();
而且get()操作得到的是Fruit及其基类:
Fruit fruit = plate.get()
Object object = plate.get()
Apple apple = plate.get() //ERROR
Plate super Fruit> plate2 = new Palte();
plate2.set(new Fruit());
plate2.set(new Apple());
plate2.set(new Object()); //ERROR
Plate> plate = new Plate();
//无界限通配符只能set null,而其get的也都是Object