泛型的定义
public class Demo1 {
public static void main(String[] args) {
Pair pair = new Pair<>();
pair.first = 1;
pair.second = 2;
pair.third = "3";
}
}
class Pair {
public T first;
public T second;
public V third;
}
2.定义泛型方法(调用时不用指定类型,除非编译器无法推断出时)
class Methods{
public static T getName(T info) {
return info;
}
}
Methods methods = new Methods<>();
Object o = Methods.getName(5);
System.out.println(""+ o.getClass().getSimpleName());//输出Integer
当在泛型类中定义泛型方法时 泛型方法应与类的泛型标记不一致,否则编译器会提示警告, 但实际匹配仍以泛型方法运行时检测类型为准
class Methods
{ public T getName(T info) { return info; } }
泛型的限定
class Animal implements Common{
void speak(){
System.out.println("speak");
}
@Override
public void who() {
System.out.println("Animal");
}
}
interface Common {
void who();
}
Test.test(new Animal());
泛型代码与虚拟机
类型擦除
翻译泛型表达式
Pair pair ...
String s = pair.getKey();
-----------------------------
编译器会翻译成
String s = (String) pair.getKey();
编译器会翻译成两条指令:
- 对原始方法的调用;
- 返回的Object强转为指定类型;
翻译泛型方法
class Child extends Root {
public void setNum(Integer a) {
super.setNum(a);
}
}
class Root {
private T num;
public void setNum(T num) {
this.num = num;
}
}
Child child = new Child();
Root root = child; //多态
root.setNum(10);
编译器会翻译成
class Child extends Root {
public void setNum(Integer a) {
super.setNum(a);
}
}
class Root {
private Object num;
public void setNum(Object num) {
this.num = num;
}
}
由于super调用的是重写方法,但重写必须满足以下要求
- 参数列表必须完全与被重写方法的相同
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个方法,则不能重写这个方法。
我再补充一下重载
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
重写与重载之间的区别
区别 | 重载 | 重写 |
---|---|---|
参数列表 | 必须修改 | 不能更改 |
返回类型 | 可以修改 | 不能更改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
所以super无法访问父类的
public void setNum(Integer a)
因此,编译器会在子类中自动生成一种桥方法
class Child extends Root {
public void setNum(Integer a) {
super.setNum(a);
}
public void setNum(Object a) {
setNum((Integet)a);
}
/*
不能自定义桥方法 否则编译器报错
//ERROR
public void setNum(Object a) {
setNum((Integet)a);
}
*/
}
Java泛型转换:
约束与局限性
1.不能用基本类型实例化类型参数 2.运行时类型查询只适用于原始类型
class Things { }
class Animal { }
class People { }
Things things = new Things<>();
Things peopleThings = new Things<>();
System.out.println(""+things.getClass().getName());// Things
System.out.println(""+peopleThings.getClass().getName());// Things
boolean a = things instanceof Things; // 编译器报错 不允许这样操作
boolean b = things instanceof Things; // 编译器提醒一直为true
3.不能创建参数化类型的数组, 不能构造泛型数组:
Things[] things;// 可以声明 但不能new
Things[] things = (Things[])new Things>[10];// 编译器报警告 但不安全
4.可变参数列表中是泛型类型的方法前需加@SafeVarargs
@SafeVarargs
private static void add(List list, T... args) {
list.addAll(Arrays.asList(args));
}
Things things1 = new Things<>();
Things things2 = new Things<>();
List> list = new ArrayList<>();
add(list, things1, things2);
5.不能实例化类型变量
class Things {
private T arg1 = new T();// 编译器报错
}
正确使用应该是利用函数接口或者使用反射(需增加try):
class Things {
private T arg1;
Things(T arg1) {
this.arg1 = arg1;
}
public void getArg1() {
System.out.println("type:"+arg1.getClass().getSimpleName()+" v"+arg1);
}
}
/**
* @param construct,表示一个无参数而且返回类型为 T 的函数
*/
private static Things getObject(Supplier construct) {
return new Things<>(construct.get());
}
/**
* 使用反射方法 添加Try Catch
*/
private static Things getObject(Class construct) {
try {
return new Things<>(construct.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
Things things = getObject(String:: new);
// 或者
Things things1 = getObject(String.class);
things.getArg1();
assert things1 != null;
things1.getArg1();
6.泛型类的静态上下文中类型变量无效(静态属性不能含有泛型) 考虑Singleton
public class Singleton {
private static T singlelnstance; // Error
public static T getSinglelnstanceO // Error
{
if (singleinstance == null) construct new instance of T
return singlelnstance;
}
}
7.不能抛出或捕获泛型类的实例
catch 子句中不能使用类型变量
泛型类扩展 Throwable 都是不合法的
在异常规范中使用类型变量是允许的
// 无法编译
public static void doWork(Class t) {
try
{
do work
}
catch (T e) // Error can 't catch type variable
{
Logger,global.info(...);
}
}
// 可以通过
public static void doWork(T t) throws T {// OK
try{
do work;
} catch (Throwable realCause){
t.initCause(realCause);
throw t;
}
}
8.可以消除对受查异常的检查
就是将所有异常都包装成指定的一个异常抛出
class Block{
@SuppressWarnings("unchecked")
public static void throwAs(Throwable e) throws T {
throw (T) e;
}
}
// 将任何异常都指定为RuntimeException
try {
do work;
}catch (Throwable t)
{
B1ock.throwAs(t) ;
}
通配符类型
- 格式: TypeClass extends Root〉
- 通配符的超类型限定 TypeClass super Manager>
- 无限定通配符 TypeClass>