首页 > 编程语言 >二-编程知识-泛型

二-编程知识-泛型

时间:2023-07-07 15:35:26浏览次数:34  
标签:知识 Parent list 编程 List add 参数 泛型 new

§ 泛型方法
一般定义如下,即方法的前面加了个

public class FClass {
	public <T> List<T> f(T t){...};
}

3种泛型参数推断方式:

1、直接在f()前面加确定泛型
fClass.<Integer>f(xxx)

2、通过输入参数确定, 下面这个推断为Integer
int number = 0;
fClass.f(number)

3、通过 返回值 确定
List<Integer> list = fClass.f(xxx);

Q: 下面这段代码哪里有问题? 是toString()那里吗?

public class A<T> {
	public static void  test(T t){
  		System.out.println(t.toString());  
  }
}

test是static方法, 因此无法感知A<T>实例里的T
需要改成`public static <T> void test(T t)`
toString()那里没问题,toString是Object的方法。

§ 泛型参数和类型消除
Q: 泛型参数T在运行时,会变成什么?
A: 统一变成Object且不包含任何类型信息。

Q: 泛型参数T可以使用instanceof做比较吗?

class A<T> {
   void f(Object arg)
   if(arg instanceof T) {
	  ...
   }
}

A: 不能,编译器会报错。

Q: 泛型参数T可以进行new T()或者new T[]操作吗?
A: 不能,编译器会报错。
![[Pasted image 20230707150827.png]]

Q: 能调用泛型参数对象里的方法吗?T.f();
A: 只能调用Object的方法。

Q: 可以用T做强制转化吗?T t = (T)object;
A: 能运行, 但不会真正发生转型, 编译时会触发waring警告。

§ 新建泛型对象时的问题
假定有2个类, 基类Parent 和子类Child

class Parent{}
class Child extends Parent{}

回答以下问题:
Q:下面这句话有问题吗?

List<Parent> list = new ArrayList<Child>()

A:有问题,编译就错误了。 List<Parent>和ArrayList<Child>并不存在父子类的关系。

Q:这个list有什么特点?

List<? extends Parent> list = new ArrayList<Child>();

A:这个list可以调用Parent a = list.get(), 但是不能list.add(new Parent())
原因:
list.get()所做的操作是在返回时, 把内部的<? extend Parent> 强转成Parent, 是合理的,任何Parent的子类都可以转成Parent
list.add(new Parent())所做的操作是在输入时, 把外部的A转成内部的<? extend Parent>, 这是不合理的,因为我们不知道这个Parent对象可以转成哪个Parent的子类。

Q:这个list有什么特点?

List<? super Child> list = new ArrayList<Parent>();
这个list有什么特点?
list.add(new Child())
list.add(new Parent())
Parent a= list.get();
Child b = list.get()

![[Pasted image 20230707151222.png]]

Child c = list.get() 或者Parent p = list.get()所做的操作是在返回时, 把内部的<? super Child> 强转成外部的Parent或者child, 是不合理的, 因为编译器觉得child的父类 不一定 能转成parent或者child,所以禁止了这种行为( 比如parent的父类是object, 但object不一定就能转成parent或者child)
*list.add(new Child())所做的操作是在输入时, 把外部的child转成内部的<? super Child>, 这是合理的,因为child一定能转成child的父类。
java编译器关于泛型的语法糖处理,可能会更容易理解
即实际代码中,是做了类似的强转
list的add方法: add( o)实际上是 add(Object o) { child = () o; }
因此编译器必须提前认可 你输入的参数必须能 转成即一定能转成child的任一个父类
因此必须是child的子类做输入参数,才能转成child的任一父类
但child的某个父类作为输入参数的话,就不确定能否转成功,比如object是没法转成parent的。
编译器就报错了。

Q:这个list有什么特点?

List<?> list = new ArrayList<A>();

A:get和add都不行,只能做remove等无返回值无输入A的操作。
PS: 注意,不是说不能调用get或add方法, 而是调用get或add时,不能使用A这个对象去操作。
即无法做add(A) 或者 A a = get(0)
但是可以做add(object) 或者Object o = get(0)
因为?可以转为Object, 但是无法转为A。

Q:下面这个代码会报错吗?

   List<Fruit> fruitList = new ArrayList<>();
   fruitList.add(new Fruit());
   List<Apple> appleList = new ArrayList<>();
   appleList.add(new Apple());
   fruitList.addAll(appleList);
   System.out.println(fruitList);

PECS原则
注意PECS原则和上面的区别!
上面之前提到的? extend或者? supert, 都是在声明对象的时候用的。
而PECS原则是用于泛型对象的方法输入参数!
假设有一个类定义如下:

public static class MyList<T> {
    List<T> list = new ArrayList<>();
    // 把输入参数塞给自己,类似于生产操作
    public void pushList(List<T> t) {
        list.addAll(t);
    }
    // 把自己的内容塞给输入参数,类似于让输入参数做消费。
    public void pollList(List<T> t) {
         t.addAll(list);
    }
}

则T就是泛型参数。

Q:下面代码能正常运行吗?

MyList<Number> myList = new MyList<>();
List<Integer> intList = new ArrayList<>();
myList.pushList(intList);
List<Object> objectList = new ArrayList<>();
myList.pollList(objectList);

A:不能正常运行, pushList和pollList都会报错
因为编译器检查后,认为 List<Integer>和List<Number>不是一个东西!

Q: 如果上文要支持pushList,应该怎么修改pushList方法的定义?
A:改成这样:

// 把输入参数塞给自己,类似于生产操作
public void pushList(List<? extends T> t) {
    list.addAll(t);
}
即编译器认为,List<Integer> 和List<? extend Number>是一个东西,允许!

Q: 如果要支持pollList,怎么修改定义?

// 把自己的内容塞给输入参数,类似于让输入参数做消费。
public void pollList(List<? super T> t) {
    t.addAll(list);
}

因为是把自己的东西塞给输入参数, 而想要能塞进去,必须保证自己这个T,是输入参数的子类,反过来说,输入参数必须是T的父类,所以用super
于是编译器认为,List<Object> 和List<? super Number>是一个东西,允许!

PECS原则出自Effective Java, 注意只是一个编程建议而已!
如果有一个类A,泛型参数为T
如果他一般只用于接收输入容器List后,塞入自己内部的T容器, 则类A就叫生产者, 因此输入参数最好定义为<? extend T>最好, 以便能接收任何T子类的容器。
如果他一般只用于接收输入容器后List, 把自己内部的T元素塞给它, 那么这个类A就叫消费者, 输入参数最好定义为<? super T>\ 最好, 以便自己的T元素能塞给任何T元素的父类容器。

标签:知识,Parent,list,编程,List,add,参数,泛型,new
From: https://www.cnblogs.com/yuanbaobao/p/17535119.html

相关文章

  • 派盘是知识管理的底座
    知识管理是现代企业客观需求和竞争优势之一,其核心是对企业知识资产进行高效、有序、安全、可靠的管理。派盘是一款面向个人和企业的本地云存储解决方案,支持KMS知识管理系统的搭建,成为知识管理的有力工具,以下将结合派盘的功能,来探讨如何利用派盘来实现高效的知识管理。π-Disk派盘®......
  • Kotlin中的泛型:协变与逆变
    协变与逆变现在假设存在类A和类B,以及泛型类LIst<A>和泛型类LIst<B>,则协变和逆变的定义如下:协变如果A是B的子类,且List<A>是List<B>的子类,那么可以说泛型List<T>是协变的逆变如果A是B的子类,且List<B>是List<A>的子类,那么可以说泛型List<T>是逆变的Java中的泛型Java中的......
  • 20230707-编程语言的变量覆盖
    实现一个特性时,发现自定义的变量position覆盖了类的属性Position,近期发现始终存在的一个难以复现的窗口还原 BUG可能被因此修复了。也曾Debug过,但没能复现。问题的解决就是这样,只要你还惦记着,问题总会被解决。对于大小写不敏感度编程语言,尤其要注意大小写,所以我和我的朋......
  • 【雕爷学编程】Arduino动手做(136)---0.91寸OLED液晶屏模块5
    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来—小小的进步或是搞......
  • Linux之shell编程
     1.脚本格式要求2.脚本的执行方式一:输入输出重定向1.输入重定向<将/etc/fstab作为输出重新定向到cat命令上2.输出重定向>>现实当前文件下的项目,创建一个文本txt,ls命令重新定向到txt中并显示出来3.管道grep|二:shell里的特殊字符【$\'''`】1.“[双引号]、’[单......
  • 学习 Qt 编程的好书推荐
    最近一段时间,陆陆续续给大家更新了不少基于Qt开发的不错的书籍,可以说每本都不错。不过放在这一堆大家也不知道先看哪个,后看哪个?所以这块给大家列举一下学习的顺序:由浅入深。大家也可以根据自己对于Qt方面的热衷程度与想要学习的深度来决定该看到哪本书籍。1、《QtCreator快......
  • 【雕爷学编程】Arduino动手做(154)---AFMotor电机扩展板模块
    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来—小小的进步或是搞......
  • 给 Helm 修复一个 Bug - 每天5分钟玩转 GPT 编程系列(2)
    目录絮絮叨逛社区领任务复现Bug让DevChat准备Bug复现材料对着DevChat给的命令复现Bug定位Bug修复Bug琢磨下怎么修复让DevChat写一个日志脱敏函数继续让DevChat补充一个UT继续让DevChat写CommitMessage提PR打完收工絮絮叨注:这一篇文章中Bug定位的部分,也就......
  • Unity学习--C#初级编程 Awake和Start、Update和FixedUpdate的区别
    1.Awake和Start相同点:对象的生命周期内只会调用一次不同点:Awake在脚本启用或未启用后均会调用,Start只会在脚本启用后调用Awake在Start前调用理解:AwakesetAmmofortheenemy,StartallowenemytoShoot2.Update和FixedUpdateUpdate:每帧调用(每帧处理时间不一......
  • CSS基础知识(未完待续)
    前言:从本篇开始记录css的一些基础知识,但不会涉及css3,以后会单独学习记录。1.CSS简介html只关注了内容的语义,但并没有注意具体布局的美化以及样式,如果用html去设置的话,就会显得十分臃肿,这时候就需要css来进行设置了。CSS是层叠样式表的简称,有时候我们也会称之为css样式表或者......