[笔记] 疯狂JAVA讲义(第3版)第9章 泛型

网友投稿 247 2022-11-16

[笔记] 疯狂JAVA讲义(第3版)第9章 泛型

第9章 泛型

泛型很大程度上是为了让集合能记住其元素的数据类型。

9.1 泛型入门

9.1.1 编译时不检查类型的异常

//ListErr.javapackage ch9;import java.util.ArrayList;import java.util.List;public class ListErr { public static void main(String[] args) { //想创建一个字符串List List strList = new ArrayList(); strList.add("疯狂java"); strList.add("疯狂android"); //“不小心”将一个Integer对象 丢进集合 strList.add(5); strList.forEach(str->System.out.println(((String)str).length()));//类型转换异常 }}

当程序“不小心”把一个Integer对象丢进List集合,导致强制类型转换成String时引发ClassCastException异常。

9.1.2 使用泛型

java5 后引入了“参数化类型”(parameterized type)的概念,允许程序在创建集合时指定集合元素的类型。如​​List​​​,表明该List只能存放String类型对象。 Java的参数化类型被称为泛型(Generic)。

对于前面的ListErr程序,可以使用泛型改进。

//创建集合,只能保存字符串。放入其他类型时会引起编译错误List strList = new ArrayList();

9.1.3 Java7泛型的“菱形”语法

java7 之前使用泛型的接口、变量时,构造器也必须带泛型:

List strList = new ArrayList();

Java7之后,允许省略构造器后面的泛型信息,只给出<>即可:

List strList = new ArrayList<>();

9.2 深入泛型

9.2.1 定义泛型接口、类

//Apple.javapackage ch9;public class Apple { private T info; public Apple() {} public Apple(T info) { this.info = info; } public T getInfo() { return info; } public void setInfo(T info) { this.info = info; } public static void main(String[] args) { Applea1 = new Apple<>("苹果"); System.out.println(a1.getInfo()); Applea2 = new Apple<>(5.67); System.out.println(a2.getInfo()); } }

9.2.2 从泛型类派生子类

从泛型类、接口派生时,父类不能再包含类型形参:

//错误,父类不能带类型形参public class A extends Apple{}

如果想从Apple派生出一个子类,可以使用如方式:

public class A extends Apple

也可以是:

public class A extends Apple

如果从​​Apple​​派生子类,Apple类中的T都会被替换成String。

如果使用Apple类时没有传入实际类型参数,java编译器可能会发出警告:使用未经检查或不安全的操作。此时,系统会把​​Apple​​的T当成Object类型处理。

9.2.3 并不存在泛型类

不管为泛型的类型形参传入哪一种形参,对于Java来说,它们仍然被当成同一个类处理,在内存中也只占用一块内存空间。 因此,静态方法、静态变量、静态初始化块中不允许使用类型形参。

由于系统并没有真正生成泛型类,所以instanceof也不能用于泛型类。

9.3 类型通配符

9.3.1 使用类型通配符

为了表示各种泛型List的父类,可以使用类型通配符,类型通配符是一个问号?,这个问号表示可以匹配任何类型:

public void test(List c){ for(int i = 0; i < c.size(); i++){ System.out.println(c.get(i)); }}

可以使用任何类型的List来调用它。

但这种List仅表示它是各种List的父类,并不能把元素加入其中,

List c = new ArrayList();//下面语句引起编译错误c.add(new Object());

因为无法确定c的类型,所以不能向其中添加对象。

9.3.2 设定类型通配符的上限

如果程序希望List 只是某一类泛型List的父类:

//它表示所有Shape泛型List的父类List

类似地,由于无法确定这个受限制通配符的具体类型,所以不能添加对象进这个集合。

9.3.3 设定类型形参的上限

表示传入的类型形参是上限类型或是上限类型的子类:

//传入的类型形参是Number类或是Number的子类public class Apple{T col;...}

另一个更严格地情况是,类型形参有多个上限:(使用&符号连接多个上限)

T必须是Number或Number子类,并且必须实现ava.io.Serializable接口public class Apple

9.4 泛型方法

9.4.1 定义泛型方法

修饰符 返回值类型 方法名(形参列表){ //方法体...}

//GenericMethodTest.javapackage ch9;import java.util.ArrayList;import java.util.Collection;public class GenericMethodTest { static void fromArrayToCollection(T[] a,Collection c) { for(T o: a) { c.add(o); } } public static void main(String[] args) { Object[] oa = new Object[10]; Collection co = new ArrayList<>(); fromArrayToCollection(oa, co); String[] sa = new String[10]; Collection cs = new ArrayList<>(); fromArrayToCollection(sa, cs); //T是Object fromArrayToCollection(sa, co); }}

方法中的泛型形参无需显式传入实际类型参数,调用时根据实参推断类型形参的值。

9.4.2 泛型方法和类型通配符的区别

大多数时候都可以使用泛型方法来代替类型通配符

public interface Collection{boolean containsAll(Collection c);boolean addAll(Collection c);}

public interface Collection{boolean containsAll(Collection c); boolean addAll(Collection c);}

类型通配符:类型形参T的唯一效果是可以在不同调用点传入不同的实际类型。

泛型方法允许类型形参表示方法的一个或多个参数之间的类型依赖关系,或是方法返回值与参数之间的类型依赖关系。如果没有这种关系,就不该用泛型方法。

如果需要,也可以同时使用泛型方法和通配符:

public staticvoid copy(Listdest, Listsrc)

9.4.3 Java 7 的菱形语法与泛型构造器

9.4.4 设定通配符下限

表示是Type或Type父类

9.4.5 泛型方法与方法重载

public staticvoid copy(Collectiondest, Collectionsrc) public static T copy(Collectiondest, Collection T>src)

调用copy时会引起编译错误。

9.4.6 Java 8 改进的类型推断

1、根据调用方法的上下文推断参数的目标类型

2、在方法调用链中,将推断得到的类型参数传递到最后一个方法。

需要注意的是,这种推断并不万能,有些地方要手动指定类型参数。

9.5 擦除和转换

9.6 泛型和数组

数组元素的类型不能包含类型变量或类型形参,除非是无上限的类型通配符。

简单来说,不要创建泛型数组。

9.7 小结

奇怪的知识增加了。

很多奇怪的用法,奇怪的错误,比如9.5和9.6节。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
下一篇:基于通用变频器+单片机实现楼宇恒压控制供水监控系统的设计
相关文章

 发表评论

暂时没有评论,来抢沙发吧~