泛型接口
a. 接口上自定义的泛型参数的具体数据类型,是在 其实现类 实现该接口 的时候指定的。
b. 若在实现该泛型接口的时候,未指定具体的数据类型,则默认为OBJECT
c. 有些时候我们在定义一个类去实现泛型接口时,我们并不确定这个类将要实现哪种类型的类,这时我们就不能确定接口中的泛型,那么接口中的泛型尚未确认,这时就要求这个类也必须定义泛型,而且泛型名称要一致,在实例化这个类的对象时,需要指定该泛型的类型,若不指定,默认为OBJECT
泛型类
当类中要操作的数据类型不确定的时候,可以定义泛型类(集合体系中的类)。格式如下:
泛型类与泛型方法
泛型类在声明对象的时候,就已经定义好了能操作的数据类型,因此,在调用该类的方法时,不可避免的限定了方法的参数类型。这时,定义泛型方法就比较方便。泛型类中也可以定义泛型方法。
静态方法不可以访问类上定义的泛型,如果静态方法操作的数据类型不确定,可以将该方法定义为泛型方法。
泛型方法
形式: public <T> void show(T t){.....}
public static <W> int sum(W w){.....}
必须放在修饰符之后,返回值之前。
package genericType;
import testkit.Lf;
/**
* @author: LiFeng
* 1. 定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,doSome3()就不是一个泛型方法
* 2. 是否是泛型方法与该类是否是泛型类无关,即该类即使不存在泛型E,doSome()和doSome1()依然是成立的泛型方法
* 3. 既然是泛型方法,就代表着我们不知道具体的类型是什么,因此无法对参数类型指定前对其做具体操作
* 优势:泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;
* 而泛型方法可以在调用的时候指明类型,更加灵活。
* 劣势:泛型方法的劣势也很明显,太过泛化,导致无法限制实参范围,无法在该方法被调用之前获知实参的具体属性,
* 所以也就无法在方法体内部对参数t 做一些具体操作,只能当作Object处理。
*/
public class GTest3<E> {
public <T> T doSome(Class<T> t) throws InstantiationException, IllegalAccessException {
T instance = t.newInstance();
return instance;
}
public <T> T doSome1(T t) {
return t;
}
public <F> E doSome2(Class<F> f, E e) {
return e;
}
public void doSome3(E e) {
}
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
GTest3<String> gt3 = new GTest3<>();
Lf return1 = gt3.doSome(Lf.class);
Object return2 = gt3.doSome(Class.forName("testkit.Lf"));
Lf return3 = gt3.doSome1(new Lf());
System.out.println(return1);
System.out.println(return2);
System.out.println(return3);
}
}
泛型方法补充:
泛型方法并不一定需要泛型参数,或者说不一定需要将泛型声明T作为参数传入,如下例方法,在调用deserialize反序列化方法时,其返回值我们可以定义为任意类型,这不会导致任何编译报错,一个简单的测试demo如下,这就体现了泛型的好处。但是在运行时需要我们更加小心,避免类型转换异常。
对于静态的泛型方法,再举一些例子
public final class Optional<T> {
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
}
package future;
/**
* @author LiFeng
* @create 2019-11-03 上午 11:40
* @describe
*/
public class Test1122 {
public static <S> S empty(Object obj) {
return (S) obj;
}
public static void main(String[] args) {
// 不指定泛型,默认返回值为Object
Object empty3 = empty("sss");
// 通过返回值指定泛型,编译器能够识别通过,但是真正执行过程中,如果不注意,是会出现强转异常的
// int i = empty("sss"); 编译时通过。运行时无法通过会报错
int i = empty(2);
// 如果仅仅是调用而不使用其返回值的话,运行时也并不会报错。
Test1122.<Integer>empty("aaa");
// 试图使用方法返回值时,会将其转换为String类型,这里会出强转异常
// Test1122.<String>empty(1).length();
// 下面这两种效果一样,尽管后者在写法上比较特殊,但都是为了使编译器能够识别,让其得知我们所需要的返回值类型
String string = empty("aaa");
Test1122.<String>empty("aaa").length(); // 即使是本类中,这种写法强制一定要加上类名进行调用,Test1122不可省略
}
}
public <T> T deserialize(byte[] bytes) {
if (bytes == null) {
return null;
}
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
bais = new ByteArrayInputStream(bytes);
ois = new ObjectInputStream(bais);
return (T) ois.readObject();
} catch (ClassNotFoundException e) {
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
}
}
if (bais != null) {
try {
bais.close();
} catch (IOException e) {
}
}
}
}
对于泛型限定,列举一个略微绕的例子:
interface ISortVO<S extends ISortVO<S>> extends Comparable<S>{
}
class A implements ISortVO<B>{
@Override
public int compareTo(B o) {
return 0;
}
}
class B implements ISortVO<B>{
@Override
public int compareTo(B o) {
return 0;
}
}
class C implements ISortVO<B>{
@Override
public int compareTo(B o) {
return 0;
}
}
在这个例子中,其实看明白了也挺简单,ISortVo 的泛型必须是符合类B这种格式的实现类,A和C是不满足泛型要求的。