一、泛型类、泛型接口和泛型方法
1、泛型接口的定义:
interface FangXingJieKou<T> {
public T useMethod();
}
interface FangXingJieKou<T,K> {
public T useMethod();
}
2、两种泛型类的定义:
(1)
class FangXingLei implements FangXingJieKou<String> {
@Override
public String useMethod() {
return null;
}
}
(2)
class FangXingLei<T> implements FangXingJieKou<T> {
@Override
public T useMethod() {
return null;
}
}
3、泛型方法的定义,可以定义在普通类中:
class Test {
public <T> T addFangxing(T... a) {
return a[a.length / 2];
}
}
也可以使用在泛型类中:
class Test <T>{
public <T> T addFangxing(T... a) {//方法中定义的类型T和类定义的T可以一样,也可以不一样,相互不影响
return a[a.length / 2];
}
}
这不是一个泛型方法,只是使用了泛型类作为了形参
class Test {
public void show(Test <Number> a) {
}
}
4、泛型方法和泛型类、泛型接口的区别
泛型类是在创建对象的时候声明泛型的类型,而泛型方法是使用的时候传入使用的类型。
二、如何限定类型变量
extends,后面可以传入类也可以传入接口,可以传入一个,也可以传入多个。只能有一个类(java单继承),或多个接口
interface FangXingFangFa {
public <T extends Fruit> void useMethod();
}
使用了extends,限定了只能传入Firut本身及其子类
如果是类和接口混用的话,类一定要写在前面,用&连接:
interface FangXingFangFa {
public <T extends ArrayList&Comparable> void useMethod();
}
泛型类中也是一样使用。
三、泛型使用中的约束和局限性
1、不能实例化类型变量:
class FangXingFangLei<T> {
private T t;
public void use(){
T t1= new T();//报错
}
}
2、静态域或者静态方法中是不能引用类型变量:
在对象创建的时候才知道泛型的类型是什么,而虚拟机在创建对象的时候,先执行了static。
class FangXingFangLei<T> {
private static T t;//报错
public static void use(T t){//报错
}
public static <T> void use(T t){//可以运行,静态方法本身是泛型方法就可以
}
}
3、不能使用基本类型,只能使用包装类
FangXingFangLei<double>//不可以
FangXingFangLei<Double>//可以
4、不支持instanceof关键字
public static <T> void use(T t) {
FangXingFangLei<Double> doubleFangXingFangLei = null;
if (doubleFangXingFangLei instanceof FangXingFangLei<Double>){//报错
}
if (doubleFangXingFangLei instanceof FangXingFangLei<T>){//报错
}
}
public static <T> void use(T t) {
FangXingFangLei<Double> doubleFangXingFangLei = new FangXingFangLei<>();
FangXingFangLei<String> stringFangXingFangLei = new FangXingFangLei<>();
if (doubleFangXingFangLei.getClass() == stringFangXingFangLei.getClass()) {
//是true,不管传入的是哪个类型参数,获得的都是原生类型,泛型类型擦除,
}
}
5、不允许初始化数组
public static <T> void use2(T t) {
FangXingFangLei<Double>[] doubleFangXingFangLeiArray;//定义数组可以,
doubleFangXingFangLeiArray = new FangXingFangLei<Double>[];//但不允许初始化
}
6、泛型类不能extends Exception/Throwable
public class FangXingFangLei<T> extends Exception{//报错
}
public static <T extends Throwable> void use3(T t) {
try {
} catch (T t) {//报错
}
}
public static <T extends Throwable> void use4(T t) throws T {//可以运行
try {
} catch (Exception e) {
throw t;
}
}
四、泛型类型的继承规则
public class Fruit {
public static class Apple extends Fruit {
}
public class Test<T> {
}
public void use() {
// Test<Apple> 和 Test<Fruit>没有任何继承关系
Test<Apple> appleTest = new Test<>();
Test<Fruit> fruitTest = new Test<>();
Fruit fruit = new Apple();//在java中这个是可以的
Test<Fruit> fruit2 = new Test<Apple>();//报错
}
}
泛型类可以继承或者扩展其他泛型类,如list和arraylist
public class Test<T> extends TestParent<T>{
}
public class TestParent<T> {
}
public void use() {
TestParent<Apple> testParent = new Test();//完全没问题
}
五、泛型中的通配符类型
1、先定义一个泛型类
public class FangXingLei<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
2、再定义一个Food类
public class Food {
public static class Fruit extends Food{
public static class Apple extends Fruit{
public static class HongFuShi extends Apple{
}
}
public static class Orange {
}
}
}
3、使用类
public class UseTongPeiFu {
public void printExtends(FangXingLei<? extends Food.Fruit> fangXingLei) {
}
public void printSuper(FangXingLei<? super Food.Fruit> fangXingLei) {
}
/**
* 使用通配符方法
*/
public void useTongPeiFuMethod() {
//extends限定了上限,泛型可以传入Food.Fruit本身及其子类
printExtends(new FangXingLei<Food>());//报错
printExtends(new FangXingLei<Food.Fruit>());//可以运行
printExtends(new FangXingLei<Food.Fruit.Apple>());//可以运行
printExtends(new FangXingLei<Food.Fruit.Apple.HongFuShi>());//可以运行
printExtends(new FangXingLei<Food.Fruit.Orange>());//报错
//super限定了下限,泛型可以传入Food.Fruit本身及其父类
printSuper(new FangXingLei<Food>());//可以运行,
printSuper(new FangXingLei<Food.Fruit>());//可以运行,
printSuper(new FangXingLei<Food.Fruit.Apple>());//报错,
printSuper(new FangXingLei<Food.Fruit.Apple.HongFuShi>());//报错
printSuper(new FangXingLei<Food.Fruit.Orange>());//报错。
}
/**
* 使用通配符对象
*/
public void useTongPeiFuBean(){
//通配符<? extend T>,只能传入T或者T的子类
FangXingLei<? extends Food.Fruit> fruitExtends = new FangXingLei<>();
//不能使用set方法,可以理解为,不能确定fruitExtends泛型是Fruit还是它的子类
fruitExtends.setT(new Food());//报错,
fruitExtends.setT(new Food.Fruit());//报错,
fruitExtends.setT(new Food.Fruit.Apple());//报错,
fruitExtends.setT(new Food.Fruit.Apple.HongFuShi());//报错,
fruitExtends.setT(new Food.Fruit.Orange());//报错
//可以使用get方法,只能确定是到自己,不能确定是哪个子类或者自己
Food.Fruit t = fruitExtends.getT();
//通配符<? super T>,只能传入T或者T的父类
FangXingLei<? super Food.Fruit> fruitSuper = new FangXingLei<>();
//只能使用自己及子类对象(水果、苹果、红富士),因为自己及子类对象都是水果。
fruitSuper.setT(new Food());//报错,
fruitSuper.setT(new Food.Fruit());//可以运行,
fruitSuper.setT(new Food.Fruit.Apple());//可以运行,
fruitSuper.setT(new Food.Fruit.Apple.HongFuShi());//可以运行,
fruitSuper.setT(new Food.Fruit.Orange());//报错,
//使用get方法,只知道是自己或者父类对象,不能确定是哪个父类或者自己,所以就只能判断是object
Object t1 = fruitSuper.getT();
}
}
总结:
extend和super两者最大的区别是extend可以安全的访问(get)数据,super可以安全的写入数据。
六、虚拟机是如何实现泛型的
类型擦除,可以理解为把类型转换成object。如果有extends的话,将第一个作为其原生类型
网友评论