【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)
泛型(Generics)
- 泛型(Generics)
- 泛型类型(Generic Type)
- 多个类型参数
- 泛型类型的继承
- 原始类型(Raw Type)
- 泛型方法(Generic Method)
- 泛型方法 - 类型推断
- 泛型方法 – 构造方法
- 限制类型参数
- 限制类型参数 — 接收泛型数组返元素最大值方法
- 限制类型参数 — 要求传入的类型必须可比较
- 通配符(Wildcards)
- 通配符 — 上界(`extends`)
- 通配符 — 下界(`super`)
- 通配符 — 无限制
- 通配符 – 继承
- 通配符 — 注意点
- 泛型的使用限制
Java笔记目录可以点这里:Java 强化笔记(适合有基础的童鞋,不适合小白)
泛型(Generics)
从 Java 5 开始,增加了泛型技术
什么是 泛型?
- 将类型变为参数,提高代码复用率
建议的类型参数名称:
• T :Type
• E :Element
• K :Key
• N :Number
• V :Value
• S、U、V :2nd, 3rd, 4th types
泛型类型(Generic Type)
什么是泛型类型?
- 使用了泛型的类或者接口
比如:java.util.Comparator
、java.util.Comparable
public class Student <T> {private T score;public T getScore() {return score;}public void setScore() {this.score = score;}
}
// Java 7 以前的写法
Student<String> stu = new Student<String>();// Java 7开始, 可以省略右边<>中的类型(钻石语法)
Student<String> stu1 = new Student<>();
stu1.setScore("A");
String score1 = stu1.getScore();Student<Double> stu2 = new Student<>();
stu2.setScore(98.5);
Double score2 = stu2.getScore();
多个类型参数
public class Student <N, S> {private N no;private S score;public Student(N no, S score) {this.no = no;this.score = score;}
}
Student<String, String> s1 = new Student<>("E9527", "A++");
Student<Integer, Double> s2 = new Student<>(17210224, 96.5);
泛型类型的继承
如果没有泛型类型的类之间有继承关系,加了泛型类型后不一定会保持继承关系。
例如 Integer
类继承自 Number
,但是 Box<Integer>
不继承 Box<Number>
只有泛型类型也相同时,原本有继承关系的类会继续保持继承关系。
如果拥有多个泛型类型,第一个泛型类型与父类的泛型类型相同,即可保持继承关系。
例如,下图中 MyList<String, Object>
与 List<String>
保持了继承关系。
原始类型(Raw Type)
泛型方法(Generic Method)
什么是泛型方法?
- 使用了泛型的方法(实例方法、静态方法、构造方法),比如
Arrays.sort(T[], Comparator<T>)
public class Main {public static void main(String[] args) {Student<String, String> s1 = new Student<>();// 可以像下面这么写, 但一般不会写的这么麻烦Main.<String, String>set(s1, "K99", "C++");Student<Integer, Double> s2 = new Student<>();// 编译器可以自动推断出类型参数的具体类型set(s2, 25, 99.5);}// 泛型方法(静态方法)static <T1, T2> void set(Student<T1, T2> stu, T1 no, T2 score) {stu.setNo(no);stu.setScore(score);}
}
泛型方法示例:
public class Box<E> {private E element;public Box() {}public Box(E element) {this.element = element;}
}
public class Main {public static void main(String[] args) {List<Box<Integer>> boxes = new ArrayList<>();addBox(11, boxes);addBox(22, boxes);addBox(33, boxes);}// 泛型方法(静态方法)static <T> void addBox(T element, List<Box<T>> boxes) {Box<T> box = new Box<>(element);boxes.add(box);}
}
泛型方法 - 类型推断
上图是 jdk 中 Collections
类的源码,可以看出 emptyList()
是泛型方法,可以自动推断类型。
// 根据接收的类是 List<String> 推断出了泛型是 String 类型
List<String> list1 = Collections.emptyList();
// 根据接收的类是 List<Integer> 推断出了泛型是 Integer 类型
List<Integer> list2 = Collections.emptyList();
泛型方法 – 构造方法
限制类型参数
可以通过 extends
对类型参数增加一些限制条件,比如 <T extends A>
extends
后面可以跟上类名、接口名,代表 T 必须是 A 类型,或者继承、实现 A
// <T extends Number> 表示传入的类型参数必须是Numbe及其子类
public class Person<T extends Number> {private T age;public <E> Person(T age) {this.age = age;}public int getAge() {// 传入null则视为0, 否则转为intreturn (age == null) ? 0 : age.intValue();}
}
public static void main(String[] args) {Person<Double> p1 = new Person<>(18.7);System.out.println(p1.getAge()); // 18Person<Integer> p2; // 正常运行, Integer是Number的子类// Person<String> p3; // 会报错, 因为String不是Number的子类
}
- 可以同时添加多个限制,比如
<T extends A & B & C>
,代表 T 必须同时满足 A、B、C,只能有一个类,但是可以有多个接口,类必须放在前面
限制类型参数 — 接收泛型数组返元素最大值方法
public class Main {public static void main(String[] args) {Double[] ds = {5.6, 3.4, 8.8, 4.6};System.out.println(getMax(ds)); // 8.8Integer[] is = {4, 19, 3, 28, 56};System.out.println(getMax(is)); // 56}static <T extends Comparable<T>> T getMax(T[] array) {if (array == null || array.length == 0) return null;T max = array[0];for (int i = 0; i < array.length; i++) {if (array[i] == null) continue;if (array[i].compareTo(max) <= 0) continue;max = array[i];}return max;}
}
限制类型参数 — 要求传入的类型必须可比较
public class Student <T extends Comparable<T>> implements Comparable<Student<T>> {private T score; // 传入的 T 继承了 Comparable<T>, 表示是可比较的public Student(T score) {this.score = score;}@Overridepublic int compareTo(Student<T> s) {// 传入若为null, 必然比本身小if (s == null) return 1;// 若自身的score不为null, 则调用compareTo与传入的进行比较if (score != null) return score.compareTo(s.score); // 此时, 自身的score为null, 若传入的score也为null则视为相同, 否则就是传入的大return s.score == null ? 0 : -1;}@Overridepublic String toString() {return "Student [score=" + score + "]";}
}
public class Main {public static void main(String[] args) {Student<Integer>[] stus = new Student[3];stus[0] = new Student<>(18);stus[1] = new Student<>(38);stus[2] = new Student<>(28);// Student [score=38]System.out.println(getMax(stus));}static <T extends Comparable<T>> T getMax(T[] array) {if (array == null || array.length == 0) return null;T max = array[0];for (int i = 0; i < array.length; i++) {if (array[i] == null) continue;if (array[i].compareTo(max) <= 0) continue;max = array[i];}return max;}
}
通配符(Wildcards)
- 在泛型中,问号
?
被称为是通配符 - 通常用作变量类型、返回值类型的类型参数
- 不能用作泛型方法调用、泛型类型实例化、泛型类型定义的类型参数
通配符 — 上界(extends
)
- 可以通过
extends
设置类型参数的上界
基本使用:
// 类型参数必须是Number类型或者是Number的子类型
void testUpper(Box<? extends Number> box) {}
// Integer是Number的子类
Box<Integer> p1 = null;
testUpper(p1); // 可以// Number类型可以作为参数
Box<Number> p2 = null;
testUpper(p2); // 可以// ? extends Number 表示参数是Number的子类
Box<? extends Number> p3 = null; // 可以
testUpper(p3);// ? extends Integer 表示参数是Integer的子类, Integer是Number的子类
Box<? extends Integer> p4 = null; // 可以
testUpper(p4);// String不是Number的子类, 传入testUpeer会报错
Box<String> p5 = null;
// testUpper(p5); // 报错
示例:对泛型数组的元素求和方法(传入的必须是Number的子类型)
public class Main {public static void main(String[] args) {List<Integer> is = Arrays.asList(1, 2, 3);System.out.println(sum(is)); // 6.0List<Double> ds = Arrays.asList(1.2, 2.3, 3.5);System.out.println(sum(ds)); // 7.0}static double sum(List<? extends Number> list) {double s= 0.0;for (Number n : list) {s += n.doubleValue();}return s;}}
通配符 — 下界(super
)
- 可以通过
super
设置类型参数的下界
基本使用:
// 类型参数必须是Integer类型或者是Integer的父类型
void testLower(Box<? super Integer> box) {
Box<Integer> p1 = null;
testLower(p1);Box<Number> p2 = null;
testLower(p2);Box<? super Integer> p3 = null;
testLower(p3);Box<? super Number> p4 = null;
testLower(p4);
示例:往泛型数组中添加元素(传入的必须是Integer的父类型)
public class Main {public static void main(String[] args) {List<Integer> is = new ArrayList<>();addNumbers(is); System.out.println(is); // [1, 2, 3, 4, 5]List<Number> ns = new ArrayList<>();addNumbers(ns);System.out.println(ns); // [1, 2, 3, 4, 5]}static void addNumbers(List<? super Integer> list) {for (int i = 1; i <= 5; i++) {list.add(i);}} }
通配符 — 无限制
// 类型参数是什么类型都可以
void test(Box<?> box) {}
Box<Integer> p1 = null;
Box<Integer> p2 = null;
Box<Integer> p3 = null;
Box<? extends Number> p4 = null;
Box<? super String> p5 = null;
Box<?> p6 = null;test(p1);
test(p2);
test(p3);
test(p4);
test(p5);
test(p6);
示例:打印泛型数组的元素(无限制,任何类型都可以)
public class Main {public static void main(String[] args) {List<Integer> is = Arrays.asList(1, 2, 3);printList(is); // 1 2 3List<Double> ds = Arrays.asList(1.2, 2.3, 3.5);printList(ds); // 1.2 2.3 3.5}static void printList(List<?> list) {for (Object object : list) {System.out.print(object + " ");}System.out.println();}
}
通配符 – 继承
无限制的绝对是最顶层的,其余的按正常继承关系理解即可。
通配符 — 注意点
泛型的使用限制
这里列出一些常用的误区,不必死记,过一遍理解即可。
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)相关推荐
- java的数组是对象吗_在Java中数组是原始类型还是对象?
数组被视为Java中的对象.其背后的原因是可以使用'new'关键字创建数组." new"关键字/运算符始终用于创建对象.这就是将数组视为对象的方式. 任何数组的直接父类或超类是&q ...
- 【转载】java中泛型使用详解
引入 Type接口 Class类 Method类 Field类 ParameterizedType接口 TypeVariable接口 类中定义泛型变量 方法中定义泛型变量 方法中泛型参数和泛型返回值 ...
- java使用泛型后消除泛型_如何以及何时使用泛型
java使用泛型后消除泛型 本文是我们名为" 高级Java "的学院课程的一部分. 本课程旨在帮助您最有效地使用Java. 它讨论了高级主题,包括对象创建,并发,序列化,反射等. ...
- 五分钟学Java:为什么不应该使用Java的原始类型?
在逛 Stack Overflow 的时候,发现了一些访问量像熊耳山一样高的问题,比如说这个:为什么不应该使用Java的原始类型?访问量足足有 205K+,这不得了啊!说明有很多很多的程序员被这个问题 ...
- java原始类型和引用类型_Java中的8种原始类型
java原始类型和引用类型 几年前,当我开始编辑Java Basics系列时,我认为将一些非常详细的信息拉到自己的帖子中是很有意义的. 这样,初学者的内容就更容易消化了. 首先,我将介绍有关Java的 ...
- Java中的8种原始类型
几年前,当我开始编辑Java Basics系列时,我认为将一些非常详细的信息拉到自己的帖子中是很有意义的. 这样,初学者的内容就更容易消化. 首先,我要介绍有关Java的8种原始类型的所有信息. Ja ...
- 引用类型和原始类型的对比(java)
Java 提供两种不同的类型:引用类型和原始类型(或内置类型).另外,Java 还为每个原始类型提供了封装类(Wrapper). 原始类型 封装类 ================= boolean ...
- [译]Effective Kotlin系列之考虑使用原始类型的数组优化性能(五)
翻译说明: 原标题: Effective Kotlin: Consider Arrays with primitives for performance critical processing 原文地 ...
- scala和java类_Scala(和Java)中的类和类型有什么区别?
当你说"打字"时,我会假设你的意思是静态类型.但我很快就会讨论动态类型. 静态类型是程序的一部分的属性,可以静态证明(静态意味着"不运行它").在静态类型语言中 ...
最新文章
- php mysql cms 商城,国内最常用的PHP+MySql免费CMS系统大全
- 简单完整地讲解tensorflow模型的保存和恢复
- 【Android 逆向】Android 逆向通用工具开发 ( Windows 平台静态库程序类型 | 编译逆向工具依赖的 Windows 平台静态库程序 )
- NN如何在表格数据中战胜GBDT类模型
- PostgreSQL 插入、删除、更新、事务
- 抽象类可以用new创建对象吗_宠物可以用人类的湿巾吗?猫咪有泪痕可以用纸巾擦掉吗?...
- Linux下修改Tomcat默认端口
- 【node内存泄漏耗尽之解决方法】
- CentOS 7上快速安装saltstack
- 51. 移除重复脚本(12)
- 来JavaEye两年了
- 最新高德地图使用——申请key、显示地图
- 武汉大学计算机学院徐霜,物联网工程专业建设与实践教学研究_余琍
- Raft和PBFT算法对比
- 关于安卓图片剪切造成软件崩溃问题的解决方法
- 微信公众号里的视频下载
- mysql 连续打卡天数_Sql如何统计连续打卡天数
- CCF-CSP_201812(第15次)
- 阿里出海遭遇挑战,Lazada三年换三帅仍无法制霸东南亚
- 第三章 添加背景音乐与音频剪辑