Java 泛型 三
一、泛型初衷
Java集合不会知道我们需要用它来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要就具有很好的通用性。但这样做也带来两个问题:
二、在集合中使用泛型
在集合中使用泛型后带来如下优势
–程序再也不能“不小心”把其他对象“丢进”strList集合中;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import java.util.*; 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())); // ② } } |
1
|
① "不小心" 把一个Integer对象 "丢进" 了集合 |
1
|
②引发ClassCastException异常。 |
三、什么是泛型
所谓泛型:就是允许在定义类、接口指定类型形参,这个类型形参在将在声明变量、创建对象时确定(即传入实际的类型参数,也可称为类型实参)。
四、泛型的“菱形”语法 <>
如下代码:
1
2
|
List<String> books = new ArrayList<String>(); Map<String,Integer> books = new ArrayList<String,Integer>(); |
在java 7 以前,<>中的粗体字代码都是必须的,但是现在可以不带粗体字代码。Java自动推断出ArrayList的<>里应该是String还是String,Integer。
现在改为:
1
2
|
List<String> books = new ArrayList<>(); Map<String,Integer> books = new ArrayList<>(); |
第一段和第二段代码是完全等价的。
泛型的简单应用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import java.util.*; public class DiamondTest { public static void main(String[] args) { // Java自动推断出ArrayList的<>里应该是String List<String> books = new ArrayList<>(); books.add( "疯狂Java讲义" ); books.add( "疯狂Android讲义" ); // 遍历books集合,集合元素就是String类型 books.forEach(ele -> System.out.println(ele.length())); // Java自动推断出HashMap的<>里应该是String , List<String> Map<String , List<String>> schoolsInfo = new HashMap<>(); // Java自动推断出ArrayList的<>里应该是String List<String> schools = new ArrayList<>(); schools.add( "斜月三星洞" ); schools.add( "西天取经路" ); schoolsInfo.put( "孙悟空" , schools); // 遍历Map时,Map的key是String类型,value是List<String>类型 schoolsInfo.forEach((key , value) -> System.out.println(key + "-->" + value)); } } |
五、深入泛型
1.定义泛型接口、类
定义Apple类时使用了泛型声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// 定义Apple类时使用了泛型声明 public class Apple<T> { // 使用T类型形参定义实例变量 private T info; public Apple(){} // 下面方法中使用T类型形参来定义构造器 public Apple(T info) { this .info = info; } public void setInfo(T info) { this .info = info; } public T getInfo() { return this .info; } public static void main(String[] args) { // 由于传给T形参的是String,所以构造器参数只能是String Apple<String> a1 = new Apple<>( "苹果" ); System.out.println(a1.getInfo()); // 由于传给T形参的是Double,所以构造器参数只能是Double或double Apple<Double> a2 = new Apple<>( 5.67 ); System.out.println(a2.getInfo()); } } |
2.从泛型派生子类
A1继承泛型类:
1
2
3
4
5
6
7
8
|
//使用泛型类时,为T形参传入String类类型 public class A1 extends Apple<String>{} //正确 //使用泛型类时,没有为T形参传入实际类型参数,这会产生警告:泛型检查警告,使用了未经检查或不安全的操作 public class A1 extends Apple{} //正确 //apple类不能跟类型形参 public class A1 extends Apple<T>{} //错误 |
继承Apple类,T被String代替。子类会继承到String getInfo()和void setInfo()两个方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class A1 extends Apple<String> { // 正确重写了父类的方法,返回值 // 与父类Apple<String>的返回值完全相同 public String getInfo() { return "子类" + super .getInfo(); } /* // 下面方法是错误的,重写父类方法时返回值类型不一致。从父类继承的应该是public String getinfo() public Object getInfo() { return "子类"; } */ } |
正确写法如下:
1
2
3
4
5
6
7
8
9
10
|
public class A2 extends Apple { // 重写父类的方法 public String getInfo() { // super.getInfo()方法返回值是Object类型, // 所以加toString()才返回String类型 return super .getInfo().toString(); } } |
3.并不存在泛型类
六、类型通配符
1
2
3
4
|
public void test(List<Object> c) { for ( int i= 0 ;i<c.size();i++)<br data-filtered= "filtered" > {<br data-filtered= "filtered" > Syso(c.get(i));<br data-filtered= "filtered" > }<br data-filtered= "filtered" > } |
这段代码看上去没有任何问题,方法的声明也没有任何问题。但是问题在于:调用该方法传入的实际参数的值。例如:
1
2
|
//创建一个List<String>对象 List<String> strList = new ArrayList<>();<br data-filtered= "filtered" > //将strList作为参数调用test<br data-filtered="filtered">test(strList); |
编译上面的程序,发生错误。
1
|
无法将Test中的test(java.util.list<java.lang.Object>)应用于java.util.list<java.lang.String> |
这说明List<String>对象不能被当成List<Object>对象使用,也就是说:List<String>类并不是List<Object>类的子类。
此外,数组和泛型有所不同:假设Foo是Bar的一个子类型(子类或者子接口),那么Foo[]依然是Bar[]的自类型;但G<Foo>不是G<Bar>的子类型。
七、?的用法
为了表示各种泛型List的父类,我们需要使用类型通配符,类型通配符是一个问号(?),将一个问号作为类型实参传给List集合,写作:List<?>(意思是未知类型元素的List)。这个问号(?)被称为通配符,它的元素类型可以匹配任何类型。
在“六”中的程序,将
1
2
3
4
5
6
7
8
|
public void test(List<Object> c) { for ( int i= 0 ;i<c.size();i++) { Syso(c.get(i)); } } |
改为:
1
2
3
4
5
6
7
8
|
public void test(List<?> c) { for ( int i= 0 ;i<c.size();i++) { Syso(c.get(i)); } } |
再次编译就没有了错误。
这里的?可谓什么都可以表示,是不是给它的权力太大了!! 当然我们有自己的解决办法:设定类型通配符的上限
我们需要一种泛型表示方法,它可以表示所有Shape泛型List的父类,为了满足这种需求,Java泛型提供了被限制的泛型通配符。被限制的泛型通配符的如下表示:List<? extends Shape>
1
2
3
4
5
|
// 定义一个抽象类Shape public abstract class Shape { public abstract void draw(Canvas c); } |
1
2
3
4
5
6
7
8
9
|
/ 定义Shape的子类Circle public class Circle extends Shape { // 实现画图方法,以打印字符串来模拟画图方法实现 public void draw(Canvas c) { System.out.println( "在画布" + c + "上画一个圆" ); } } |
1
2
3
4
5
6
7
8
9
|
// 定义Shape的子类Rectangle public class Rectangle extends Shape { // 实现画图方法,以打印字符串来模拟画图方法实现 public void draw(Canvas c) { System.out.println( "把一个矩形画在画布" + c + "上" ); } } |
上面定义了三个形状类,Sharp抽象父类,Circle类和Rectangle类继承了抽象类Sharp。
下面定义一个Canvas类,该画布类不同的形状。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import java.util.*; public class Canvas { // 同时在画布上绘制多个形状 public void drawAll(List< Shape> shapes) { for (Shape s : shapes) { s.draw( this ); } } public static void main(String[] args) { List<Circle> circleList = new ArrayList<Circle>(); Canvas c = new Canvas(); // 由于List<Circle>并不是List<Shape>的子类型, // 所以下面代码引发编译错误 c.drawAll(circleList); } } |
修改如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import java.util.*; public class Canvas { // 同时在画布上绘制多个形状,使用被限制的泛型通配符 public void drawAll(List<? extends Shape> shapes) { for (Shape s : shapes) { s.draw( this ); } } public static void main(String[] args) { List<Circle> circleList = new ArrayList<Circle>(); Canvas c = new Canvas(); // 由于List<Circle>并不是List<Shape>的子类型,但是使用了通配符 // 所以下面代码正确 c.drawAll(circleList); } } |
这段代码就没有了错误。
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Apple<T extends Number> { T col; public static void main(String[] args) { Apple<Integer> ai = new Apple<>(); Apple<Double> ad = new Apple<>(); // 下面代码将引起编译异常,下面代码试图把String类型传给T形参 // 但String不是Number的子类型,所以引发编译错误 Apple<String> as = new Apple<>(); // ① } } |
八、泛型方法
如果定义类、接口是没有使用类型形参,但定义方法时想自己定义类型形参,这也是可以的,JDK1.5还提供了泛型方法的支持。
九、泛型方法与类型通配符的区别
TreeSet(Comparator<? super E> c)
……待续
Java 泛型 三的更多相关文章
- Java泛型三:Java泛型详解
原文地址https://www.cnblogs.com/lzq198754/p/5780426.html 1.为什么需要泛型 泛型在Java中有很重要的地位,网上很多文章罗列各种理论,不便于理解,本篇 ...
- 一起学 Java(三) 集合框架、数据结构、泛型
一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...
- 【Java心得总结三】Java泛型上——初识泛型
一.函数参数与泛型比较 泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型.泛型的作用是什么,这里与函数参数做一个比较: 无参数的函数: public int[] newIntAr ...
- Effective Java 第三版——29. 优先考虑泛型
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 深入分析Java反射(三)-泛型
前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行 ...
- Java:泛型基础
泛型 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚很多! 解决这种限制的 ...
- 【Java心得总结四】Java泛型下——万恶的擦除
一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...
- Java—泛型
泛型是JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型.泛型本质是参数化类型,也就是所操作的数据类型指定为一个参数. 假定我们有这样一个需求: ...
- Java泛型学习笔记 - (七)浅析泛型中通配符的使用
一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...
随机推荐
- vue中的表单验证
http://www.cnblogs.com/luoxuemei/p/9295506.html /*是否合法IP地址*/ export function validateIP(rule, value, ...
- Codeforce 741B Arpa's weak amphitheater and Mehrdad's valuable Hoses(并查集&分组背包)
题意: 给定n个价值为b 花费为w的物品, 然后某些物品是属于同一个组的, 给定一个花费限制V, 求在小于等于V的情况下取得到的价值最大为多少,能对于同一个组的物品,要么全取,要么只取一个. 分析: ...
- HDU-5583-Kingdom of Black and White(2015ACM/ICPC亚洲区上海站-重现赛)
Kingdom of Black and White ...
- 7-16 一元多项式求导(20 分)(有关while(scanf("%d",&n)!=EOF))
7-16 一元多项式求导(20 分) 设计函数求一元多项式的导数. 输入格式: 以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过1000的整数).数字间以空格分隔. 输出格式: 以与输入相同 ...
- SpringBoot Data JPA 关联表查询的方法
SpringBoot Data JPA实现 一对多.多对一关联表查询 开发环境 IDEA 2017.1 Java1.8 SpringBoot 2.0 MySQL 5.X 功能需求 通过关联关系查询商店 ...
- bzoj3545 Peaks 线段树合并
离线乱搞... 也就是一个线段树合并没什么 #include<algorithm> #include<iostream> #include<cstring> #in ...
- 【HDOJ5714】拍照(线性扫描)
题意:小明在旅游的路上看到了一条美丽的河,河上有许多船只,有的船只向左航行,有的船只向右航行.小明希望拍下这一美丽的风景,并且把尽可能多的船只都完整地拍到一张照片中. 小明位于河的边上,并且可以在河边 ...
- Codeforces713D. Animals and Puzzle
$n<=1000,m<=1000$,$n*m$的01矩阵,给$t<=1000000$个询问,每次问一个矩形中最大的1正方形的边长. 先想想不考虑“一个矩形中”的限制,那记$f(i,j ...
- Codeforces 631B Print Check【模拟】
题意: 按顺序给定列和行进行涂色,输出最终得到的方格颜色分布. 分析: 记录下涂的次序,如果某个元素的横和列都被涂过,那么就选择次序最大的颜色. 代码: #include<iostream> ...
- COGS2479(四维偏序)
题意:给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 分析:cdq分治 ...