Java之泛型

一、什么是泛型
  • 泛型是jdk5引入的类型机制,就是将类型参数化,它是早在1999年就制定的JSR14的实现
  • 泛型机制将类型转换时的类型检查从运行时提前到了编译时,使用泛型编写的代码比杂乱的使用object并在需要时再强制类型转换的机制具有更好的可读性和安全性。
  • 泛型解决集合中的问题
    • 可读性,从字面上就可以判断集合中的内容类型;
    • 类型检查,避免插入非法类型。
    • 获取数据时不在需要强制类型转换。
二、泛型的格式
2.1泛型类
public class GenericityClass<T>{
	private T field;
}

其中<T> 是类型参数定义
使用的时候 : GenericityClass g = new GenericityClass();
当然这个String也可以是其他类型,包括八种包装类
此时类内部的field字段就是String类型
如果引用多个类型,可以使用逗号分隔:<S, D>

2.2泛型方法

泛型方法定义:

public class GenericityClass<T>{
	private T field;
	public static <T> T genericityMethods(T t){}
}

与泛型类一样, 是类型参数定义
调用过程: new GenericiyuClass.getMiddle(String,String,String)

当然也可以简写: new GenericiyuClass.getMiddle(String,String,String)

  • 泛型方法有自己的类型参数,泛型类的成员方法使用的是当前类的类型参数。
  • 方法中有 是泛型方法;没有的,称为泛型类中的成员方法。
2.3类型参数的限定
  • 如果限制只有特定某些类可以传入T参数,那么可以对T进行限定,如:只有实现了特定接口的类:,表示的是Comparable及其子类型。

  • 限定符可以指定多个类型参数,分隔符是 &,不是逗号,因为在类型参数定义中,逗号已经作为多个类型参数的分隔符了
    例如:<T,S extends Comparable & Serializable>

  • 泛型限定的优点:

    1. 限制某些类型的子类型可以传入,在一定程度上保证类型安全;
    2. 可以使用限定类型的方法。
public class DemoParent<T extends ArrayList<T>>{
	private T field;
	
	public T getField(){
		return field;
	}
	public void setField(T field){
		this.field = field;
	}
}
三、泛型的进阶
泛型擦除
  • 泛型只在编译阶段有效,编译后类型被擦除了,也就是说jvm中没有泛型对象,只有普通对象。所以完全可以把代码编译为jdk1.0可以运行的字节码。
擦除的方式

定义部分,即尖括号中间的部分直接擦除。

public class DemoClass<T extends Comparable> {}

引用部分如:

public T field;

其中的T被替换成对应的限定类型,擦除后:

public Comparable field;

如果没有限定类型:

public class DemoClass<T>{
  public T field;
}

那么替换为object,

public class DemoClass{
  public Object field;
}

有多个限定符的,替换为第一个限定类型名。如果引用了第二个限定符的类对象,编译器会在必要的时候进行强制类型转换。

public class DemoClass<T extends Comparable & Serializable>{
  public T field;
}

类擦除后变为:

public class DemoClass{
  public Comparable field;
}

而表达式返回值返回时,泛型的编译器自动插入强制类型转换。

泛型的约束和限制
  • 不能使用8个基本类型实例化类型参数原因在于类型擦除,Object不能存储基本类型:
  • byte,char,short,int,long,float,double,boolean
  • 从包装类角度来看,或者说三个: Number(byte,short,int,long,float,double),char,boolean
类型检查不可使用泛型
if(a instanceof Alphabet<String>){}//error
Alphabet<String> al = (Alphabet<String>) a;//warn
不能创建泛型对象数组
Generic<String>[] o=null;//ok
o=new Generic<String>[10];//error
  • 可以定义泛型类对象的数组变量,不能创建及初始化。
  • 注意:可以创建通配类型数组,然后进行强制类型转换。不过这是类型不安全的。

不可以创建的原因是:因为类型擦除的原因无法为元素赋值时类型检查,因此jdk强制不允许。
有一个特例是方法的可变参数,虽然本质上是数组,却可以使用泛型。
安全的方法是使用List。

Varargs警告

java不支持泛型类型的对象数组,可变参数是可以的。它也正是利用了强制类型转换,因此同样是类型不安全的。所以这种代码编译器会给一个警告。

@SafeVarargs  // 1.
public static <T> T getMiddleNumber(T...a){
  return a[a.length/2];
}

去除警告有两种途径:

  • 本例中的getMiddleNumber加上@SafeVarargs注解,
  • 在调用该方法时添加@SuppressWarnings(“unchecked”)注解。
不能实例化泛型对象
T t= new T();//error
T.class.newInstance();//error
T.class;//error

传入Class t参数,调用t.newInstance()可以解决此问题

public void sayQu(Class<T> c){
  T t=null;
  try {
    t=c.newInstance();
  } catch (Exception e) {
    e.printStackTrace();
  }
}
不能在泛型类的静态域中使用泛型类型
public class Singleton<T>{
    private static T singleton; //error
    public static T getInstance(){} //error
    public static void write(T t){} //error
}

静态的泛型方法可以使用泛型类型:

public static <T> T getInstance(){return null;} //ok
public static <T> void write(T t){} //ok
  1. 泛型类中,称为类型变量,实际上就相当于在类中隐形的定义了一个不可见的成员变量:private T t;,这是对象级别的,对于泛型类型变量来说是在对象初始化时才知道其具体类型的。而在静态域中,不需要对象初始化就可以调用,这是矛盾的。

  2. 静态的泛型方法,是在方法层面定义的,就是说在调用方法时,T所指的具体类型已经明确了。

通配符类型

通配符是在泛型类使用时的一种机制,不能用在泛型定义时的泛型表达式中(这是泛型类型参数限定符)。

  • 子类型通配符
    如果A是B的超类,那么 P< B > 就是P<? extends A>的子类型,通配符就是为了解决这个问题的。

  • extends 称为子类型限定通配符,又称上边界通配符(upper bound wildcard Generics),代表继承它的所有子类型,通配符匹配的类型不允许作为参数传入,只能作为返回值。

public static void demo1() {
  Parent<Integer> b = new Parent<Integer>();
  b.setAge(1);
  Parent<? extends Number> b2 = b1;
  Integer i = 100;
  b.setName(i);// 编译错误
  Number s = b2.getName();
  System.out.println(s);
}
超类型通配符

与之对应的是超类型 Pde<? super Parent>,又称下边界通配符(lower bound wildcard Generics),通配符匹配的类型可以为方法提供参数,不能得到返回值。

无限定通配符

P<?> 就是 P<? extends Object>

  • 无限定通配符可以作为返回值,不可做入参。
  • 返回值只能保存在Object中。
  • P<?> 不等于 P
  • P是P<?>的子类。

小结:

  • 限定通配符总是包括自己;
  • 子类型通配符:set方法受限,只可读,不可写;
  • 超类型通配符:get方法受限,不可读(Object除外),只可写;
  • 无限定通配符,只可读不可写;
  • 不可同时声明子类型和超类型限定符,及extends和super只能出现一个。

学习各路大神再加上自己的一些总结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值