Java高级
- Java高级语言特性
- 一. 泛型
- 1. 1 为什么我们需要泛型
- 1. 2 泛型类和泛型接口的定义
- 1. 3 泛型方法
- 1. 4 限定类型变量
- 1. 5 泛型中的约束和局限性
- 1. 6 泛型中的继承规则
- 1. 7 通配符类型
- 1.7.1 问题抛出,为啥需要通配符?
- 1.7.2 `? extends X`
- 1.7.3 `? super X`
- 1.7.4 无限定通配符
- 1. 7 虚拟机是如何实现泛型的?
Java高级语言特性
一. 泛型
1. 1 为什么我们需要泛型
- 功能1:适用于多种类型数据,执行相同的代码。
- 功能2:在编码的时候,可以对这一段功能代码的数据类型进行指定,不需要强制类型转换。
1. 2 泛型类和泛型接口的定义
- 参数化类型,将所需要传入的数据类型参数化了。比如:int、double、float都可以看成一种数据类型,可以使用泛型代替他们所有。
泛型类的使用
package com.itheima.reggie.genmenthod;
//这里定义的是T,说明了这里传入的参数T可以是任何的类型,需要什么类型,就可以设置成什么类型
public class NormalGeneric<T> {
private T data;
//构造函数
public NormalGeneric(){
this.data = data;
}
//get函数
public T getData(){
return data;
}
//set函数
public void setData(T data){
this.data = data;
}
public static void main(String[] args) {
//这里注意:这里的泛型T指定为String类型,这就实现了功能2,对一段功能代码进行指定参数类型。
NormalGeneric<String> normalGeneric = new NormalGeneric<>();
normalGeneric.setData("ok");
System.out.println(normalGeneric.getData());
}
}
泛型接口的使用
泛型接口1.
//不需要指定所实现的方法的具体类型
package com.itheima.reggie.genmenthod;
public class ImplGenerator<T> implements Generator<T>{
public T next(){
return null;
}
}
泛型接口2.
package com.itheima.reggie.genmenthod;
public class ImplGenerator2 implements Generator<String>{
@Override //大家可以看到,当使用的泛型接口被指定了为某一个类型的时候,继承类中的方法就得使用所指定的类型
public String next(){
return null;
}
}
1. 3 泛型方法
普通方法:
这里就是一个普通的方法,因为只是利用了泛型类中的泛型,而不是泛型方法的泛型
泛型方法:
泛型方法加强:
package cn.enjoyedu.generic.defgeneric.genmethod;
/**
* 类说明:
*/
public class GenericMethod3 {
static class Fruit{
@Override
public String toString() {
return "fruit";
}
}
static class Apple extends Fruit{
@Override
public String toString() {
return "apple";
}
}
static class Person{
@Override
public String toString() {
return "Person";
}
}
static class GenerateTest<T>{
//普通方法
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。
//可以类型与T相同,也可以不同。
//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,
//编译器也能够正确识别泛型方法中识别的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型T,
//注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
public <T> void show_2(T t){
System.out.println(t.toString());
}
}
public static void main(String[] args) {
Apple apple = new Apple();
Person person = new Person();
//定义GenerateTest中的T是Fruit类型的
GenerateTest<Fruit> generateTest = new GenerateTest<>();
generateTest.show_1(apple);
//generateTest.show_1(person);
//相当于show_2和Fruit是没有关系的了
generateTest.show_2(apple);
generateTest.show_2(person);
//这里的show_3和show_2实际上是一样的
generateTest.show_3(apple);
generateTest.show_3(person);
}
}
泛型类中定义的泛型仅仅影响类中的普通的方法,对泛型方法是没有影响的。
1. 4 限定类型变量
- 有时候,我们需要对类型变量加以约束,比如计算两个变量的最小,最大值。
- 请问,如果确保传入的两个变量一定有compareTo方法?那么解决这个问题的方案就是将T限制为实现了接口Comparable的类,
T extends Comparable
中:
- T表示应该绑定类型的子类型,Comparable表示绑定类型,子类型和绑定类型可以是类也可以是接口。
如果这个时候,我们试图传入一个没有实现接口Comparable的类的实例,将会发生编译错误。
- 同时extends左右都允许有多个,如 T,V extends Comparable & Serializable。
- 注意限定类型中,只允许有一个类,而且如果有类,这个类必须是限定列表的第一个。
- 这种类的限定既可以用在泛型方法上也可以用在泛型类上。
1. 5 泛型中的约束和局限性
- 现在我们有泛型类:
public class Restrict<T> { 泛型类
- 不能用基本类型实例化类型参数:
Restrict<double> 这种是不可行的
Restrict<Double> restrict = new Restrict<>(); 这种是可以的,必须要大写才行
- 运行时类型查询只适用于原始类型
if(restrict instanceof Restrict<Double>) 这种是不可行的
if(restrict instanceof Restrict<T>) 这种是不可行的
Restrict<String> restrictString= new Restrict<>(); 这是可行的
System.out.println(restrict.getClass()==restrictString.getClass()); true
System.out.println(restrict.getClass().getName());
System.out.println(restrictString.getClass().getName());
- 不能实例化类型变量
public Restrict() {
this.data = new T(); 不能实例化类型变量,这种是不可以的
}
- 静态域或者方法里不能引用类型变量
private static T instance; 这种是不可以的
private static <T> T getInstance(){}; 这种是可以的
- 不能创建参数化类型的数组(可定义,但是不能初始化)
Restrict<Double>[] restrictArray; 这是可以的
Restrict<Double>[] restricts = new Restrict<Double>[10]; 这是不可以的
- 泛型不能继承
Exception/Throwable
private class Problem<T> extends Exception; 这是不行的
/*不能捕获泛型类对象*/
// public <T extends Throwable> void doWork(T x){
// try{
//
// }catch(T x){
// //do sth;
// }
// }
这是可行的
public <T extends Throwable> void doWorkSuccess(T x) throws T{
try{
}catch(Throwable e){
throw x;
}
}
1. 6 泛型中的继承规则
-
Pair<Employee>
和Pair<Worker>
没有继承关系 -
泛型类可以继承或者扩展其他泛型类,比如
List
和ArrayList
类Employee
:
package cn.enjoyedu.generic.inherit;
/**
* @author
*/
public class Employee {
private String firstName;
private String secondName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSecondName() {
return secondName;
}
public void setSecondName(String secondName) {
this.secondName = secondName;
}
}
类Worker
继承至Employee
:
package cn.enjoyedu.generic.inherit;
/**
* @author Mark老师
*/
public class Worker extends Employee {
}
功能类Pair
:
package cn.enjoyedu.generic.inherit;
/**
* @author
*/
public class Pair<T> {
private T one;
private T two;
public T getOne() {
return one;
}
public void setOne(T one) {
this.one = one;
}
public T getTwo() {
return two;
}
public void setTwo(T two) {
this.two = two;
}
private static <T> void set(Pair<Employee> p){
}
public static void main(String[] args) {
//Pair<Employee>和Pair<Worker>没有任何继承关系
Pair<Employee> employeePair = new Pair<>();
Pair<Worker> workerPair = new Pair<>();
Employee employee = new Worker();
//Pair<Employee> employeePair2 = new Pair<Worker>();
Pair<Employee> pair = new ExtendPair<>();
set(employeePair);
//set(workerPair);
}
/*泛型类可以继承或者扩展其他泛型类,比如List和ArrayList*/
private static class ExtendPair<T> extends Pair<T>{
}
}
1. 7 通配符类型
1.7.1 问题抛出,为啥需要通配符?
- 正是因为前面所述的,
Pair<Employee>
和Pair<Worker>
没有任何关系,如果我们有一个泛型类和一个方法。
一个泛型类:
public class GenericType<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
一个方法:
public static void print(GenericType<Fruit> p){
System.out.println(p.getData().getColor());
}
- 现在我们有继承关系的类:Food Fruit Orange Apple HongFuShi
Food
Fruit
Orange
Apple
HongFuShi
wildchar
各个类的代码:
package cn.enjoyedu.generic.wildchar;
/**
* @author
*/
public class Food {
}
package cn.enjoyedu.generic.wildchar;
/**
* @author
*/
public class Fruit extends Food {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
package cn.enjoyedu.generic.wildchar;
/**
* @author Mark老师
*/
public class Orange extends Fruit {
}
package cn.enjoyedu.generic.wildchar;
/**
* @author
*/
public class Apple extends Fruit {
}
package cn.enjoyedu.generic.wildchar;
/**
* @author
*/
public class HongFuShi extends Apple {
}
package cn.enjoyedu.generic.wildchar;
/**
* @author
*/
public class WildChar{
public static void print(GenericType<Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use(){
GenericType<Fruit> a = new GenericType<>();
print(a);
GenericType<Orange> b = new GenericType<>();
//print(b);
}
public static void print2(GenericType<? extends Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use2(){
GenericType<Fruit> a = new GenericType<>();
print2(a);
GenericType<Orange> b = new GenericType<>();
print2(b);
//print2(new GenericType<Food>());
GenericType<? extends Fruit> c = new GenericType<>();
Apple apple = new Apple();
Fruit fruit = new Fruit();
//c.setData(apple);
//c.setData(fruit);
Fruit x = c.getData();
}
public static void printSuper(GenericType<? super Apple> p){
System.out.println(p.getData());
}
public static void useSuper(){
GenericType<Fruit> fruitGenericType = new GenericType<>();
GenericType<Apple> appleGenericType = new GenericType<>();
GenericType<HongFuShi> hongFuShiGenericType = new GenericType<>();
GenericType<Orange> orangeGenericType = new GenericType<>();
printSuper(fruitGenericType);
printSuper(appleGenericType);
// printSuper(hongFuShiGenericType);
// printSuper(orangeGenericType);
//表示GenericType的类型参数的下界是Apple
GenericType<? super Apple> x = new GenericType<>();
x.setData(new Apple());
x.setData(new HongFuShi());
//x.setData(new Fruit());
Object data = x.getData();
}
}
则会产生这种情况:
public static void print(GenericType<Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use(){
GenericType<Fruit> a = new GenericType<>();
print(a);
GenericType<Orange> b = new GenericType<>();
//print(b); // 这种是不被允许的
}
为解决这个问题,于是提出了一个通配符类型?
有两种使用方式:
? extends X
表示类型的上界,类型参数是X的子类
? super X
表示类型的下界,类型参数是X的超类
这两种 方式从名字上来看,特别是super,很有迷惑性,下面我们来仔细辨析这两种方法。
1.7.2 ? extends X
- 表示传递给方法的参数,必须是
X
的子类(包括X
本身)。这里的? extends Fruit
是用在方法里面的。 - 但是对泛型类
GenericType
来说,如果其中提供了get
和set
类型参数变量的方法的话,set
方法是不允许被调用的,会出现编译错误 get
方法则没问题,会返回一个Fruit
类型的值。
public static void print2(GenericType<? extends Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use2(){
GenericType<Fruit> a = new GenericType<>();
print2(a);
GenericType<Orange> b = new GenericType<>();
print2(b);
//print2(new GenericType<Food>());
GenericType<? extends Fruit> c = new GenericType<>();
Apple apple = new Apple();
Fruit fruit = new Fruit();
//c.setData(apple);
//c.setData(fruit);
Fruit x = c.getData();
}
为何?
道理很简单,? extends X
表示类型的上界,类型参数是X的子类,那么可以肯定的说,get
方法返回的一定是个X
(不管是X或者X
的子类)编译器是可以确定知道的。但是set
方法只知道传入的是个X
,至于具体是X的那个子类,不知道。
总结:主要用于安全地访问数据,可以访问X
及其子类型,并且不能写入非null
的数据。
1.7.3 ? super X
- 表示传递给方法的参数,必须是
X
的超类(包括X
本身) - 但是对泛型类
GenericType
来说,如果其中提供了get
和set
类型参数变量的方法的话,set
方法可以被调用的,且能传入的参数只能是X
或者X
的子类 get
方法只会返回一个Object
类型的值。
public static void printSuper(GenericType<? super Apple> p){
System.out.println(p.getData());
}
public static void useSuper(){
GenericType<Fruit> fruitGenericType = new GenericType<>();
GenericType<Apple> appleGenericType = new GenericType<>();
GenericType<HongFuShi> hongFuShiGenericType = new GenericType<>();
GenericType<Orange> orangeGenericType = new GenericType<>();
printSuper(fruitGenericType);
printSuper(appleGenericType);
// printSuper(hongFuShiGenericType);
// printSuper(orangeGenericType);
//表示GenericType的类型参数的下界是Apple
GenericType<? super Apple> x = new GenericType<>();
x.setData(new Apple());
x.setData(new HongFuShi());
//x.setData(new Fruit());
Object data = x.getData();
}
为何?
? super X
表示类型的下界,类型参数是X的超类(包括X
本身),那么可以肯定的说,get
方法返回的一定是个X
的超类,那么到底是哪个超类?不知道,但是可以肯定的说,Object
一定是它的超类,所以get
方法返回Object
。编译器是可以确定知道的。对于set
方法来说,编译器不知道它需要的确切类型,但是X
和X
的子类可以安全的转型为X
。
总结:主要用于安全地写入数据,可以写入X
及其子类型。
1.7.4 无限定通配符
表示对类型没有什么限制,可以把?看成所有类型的父类,Pair< ?>
;
比如:ArrayList<T> al=new ArrayList<T>();
指定集合元素只能是T类型ArrayList<?> al=new ArrayList<?>();
集合元素可以是任意类型,这种没有意义,一般是方法中,只是为了说明用法。
在使用上:? getFirst()
: 返回值只能赋给 Object
,;void setFirst(?)
: setFirst
方法不能被调用, 甚至不能用 Object
调用;
1. 7 虚拟机是如何实现泛型的?
- 后续更新