我是靠谱客的博主 眼睛大路人,最近开发中收集的这篇文章主要介绍JAVA| 泛型知识点全面总结—参考JAVA核心技术 卷一第五版,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

泛型的定义

  1. 定义泛型类
public class Demo1 {
public static void main(String[] args) {
Pair<Integer,String> pair = new Pair<>();
pair.first = 1;
pair.second = 2;
pair.third = "3";
}
}
class Pair<T, V> {
public T first;
public T second;
public V third;
}

2.定义泛型方法(调用时不用指定类型,除非编译器无法推断出时)

class Methods<T>{
public static <T> T getName(T info) {
return info;
}
}
Methods<String> methods = new Methods<>();
Object o = Methods.getName(5);
System.out.println(""+ o.getClass().getSimpleName());//输出Integer

当在泛型类中定义泛型方法时 泛型方法应与类的泛型标记不一致,否则编译器会提示警告, 但实际匹配仍以泛型方法运行时检测类型为准

class Methods<T>{
public
<T> T getName(T info) {
return info;
}
}

泛型的限定

  1. 使用关键词 extends;
  2. 如有多个可使用&分隔,并且传入的参数类型必须同时满足extends后面的类型;
  3. extends后面可以是类,抽象类,接口;
  4. 建议extends后面的类型标签接口(无方法接口)放在最后一个;
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());

泛型代码与虚拟机

类型擦除

  1. java的泛型存在于编译阶段,编译成字节码时无泛型,虚拟机里是没有泛型的;
  2. 泛型类型都会应原始类型,原始类型就是删除类型参数后的泛型类型名,擦除类型变量,并替换成限定类型(无限定的用Object);
  3. 当限定类型有多个的时候,选择第一个类型来替换;

翻译泛型表达式

Pair<String, Integer> pair ...
String s = pair.getKey();
-----------------------------
编译器会翻译成
String s = (String) pair.getKey();

编译器会翻译成两条指令:

  1. 对原始方法的调用;
  2. 返回的Object强转为指定类型;

翻译泛型方法

class Child extends Root<Integer> {
public void setNum(Integer a) {
super.setNum(a);
}
}
class Root<T> {
private T num;
public void setNum(T num) {
this.num = num;
}
}
Child child = new Child();
Root<Integer> 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. 所有的类型参数都用他们的限定类型替换;
  3. 桥方法的合成是用来保持多态;
  4. 为保证类型安全性,必要时插入强制类型转换;

约束与局限性

1.不能用基本类型实例化类型参数 2.运行时类型查询只适用于原始类型

class Things<T> { }
class Animal { }
class People { }
Things<Animal> things = new Things<>();
Things<People> peopleThings = new Things<>();
System.out.println(""+things.getClass().getName());// Things
System.out.println(""+peopleThings.getClass().getName());// Things
boolean a = things instanceof Things<Animal>; // 编译器报错 不允许这样操作
boolean b = things instanceof Things; // 编译器提醒一直为true

3.不能创建参数化类型的数组, 不能构造泛型数组:

Things<Animal>[] things;// 可以声明 但不能new
Things<Animal>[] things = (Things<Animal>[])new Things<?>[10];// 编译器报警告 但不安全

4.可变参数列表中是泛型类型的方法前需加@SafeVarargs

@SafeVarargs
private static<T> void add(List<T> list, T... args) {
list.addAll(Arrays.asList(args));
}
Things<Animal> things1 = new Things<>();
Things<Animal> things2 = new Things<>();
List<Things<Animal>> list = new ArrayList<>();
add(list, things1, things2);

5.不能实例化类型变量

class Things<T> {
private T arg1 = new T();// 编译器报错
}

正确使用应该是利用函数接口或者使用反射(需增加try):

class Things<T> {
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 <T> Things<T> getObject(Supplier<T> construct) {
return new Things<>(construct.get());
}
/**
* 使用反射方法 添加Try Catch
*/
private static <T> Things<T> getObject(Class<T> construct) {
try {
return new Things<>(construct.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
Things<String> things = getObject(String:: new);
// 或者
Things<String> things1 = getObject(String.class);
things.getArg1();
assert things1 != null;
things1.getArg1();

6.泛型类的静态上下文中类型变量无效(静态属性不能含有泛型) 考虑Singleton<Random>

public class Singleton<T> {
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 <T extends Throwable> void doWork(Class<T> t) {
try
{
do work
}
catch (T e) // Error can 't catch type variable
{
Logger,global.info(...);
}
}
// 可以通过
public static <T extends Throwable> 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 <T extends Throwable>void throwAs(Throwable e) throws T {
throw (T) e;
}
}
// 将任何异常都指定为RuntimeException
try {
do work;
}catch (Throwable t)
{
B1ock.<RuntimeException>throwAs(t) ;
}

通配符类型

  • 格式: TypeClass<? extends Root〉
  • 通配符的超类型限定 TypeClass<? super Manager> 
  • 无限定通配符 TypeClass<?>

 

最后

以上就是眼睛大路人为你收集整理的JAVA| 泛型知识点全面总结—参考JAVA核心技术 卷一第五版的全部内容,希望文章能够帮你解决JAVA| 泛型知识点全面总结—参考JAVA核心技术 卷一第五版所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部