深入理解java泛型Generic

网友投稿 277 2022-10-25


深入理解java泛型Generic

一、背景

泛型技术诞生之前(JDK5以前),创建集合的类型都是Object 类型的元素,存储内容没有限制,编译时正常,运行时容易出现ClassCastException 异常。

public class Test {

public static void main(String[] args) {

ArrayList list = new ArrayList();

list.add("java");

list.add(100);

list.add(true);

for(int i = 0;i

Object o = list.get(i);

String str = (String)o;

System.out.println(str);

}

}

}

输出:

java

Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

at com.chengyu.junit.Test.main(Test.java:18)

二、泛型概念

JDK5 中引入泛型,从而可以在编译时检测是否存在非法的类型数据结构。

其本质就是参数化类型,可以用于类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

public static void main(String[] args) {

ArrayList strList = new ArrayList();

strList.add("java");

strList.add("C#");

for(int i = 0;i < strList.size();i++) {

String str = strList.get(i);

System.out.println(str);

}

}

三、泛型类

3.1 定义与调用

定义类时设置泛型,该泛型可用于类中的属性或方法中,调用该泛型类时,指定具体类型;

// 调用泛型类

public class GenericTest {

public static void main(String[] args) {

Generic strGen = new Generic<>("a");

String key = strGen.getKey();

System.out.println(key);

}

}

// 定义泛型类

// @param 使用类时指定

class Generic{

private T key;

public Generic(T key) {

this.key = key;

}

public T getKey() {

return key;

}

public void setKey(T key) {

this.key = key;

}

@Override

public String toString() {

return "GenericTest [key=" + key + "]";

}

}

3.2 注意

1)调用泛型类时未定义类型,则会按照Object 类型处理;

2)调用时分别指定不同类型,但本质都是Object 类型;

3)泛型不支持基本数据类型;

4)泛型类的成员方法不可以用static 修饰(泛型方法可以)。

3.3 使用

需求:抽奖活动,但抽奖内容没有确定,可能都是现金,也可能都是物品

public class ProductGetterTest {

public static void main(String[] args) {

// 抽物品

ProductGetter strProductGetter = new ProductGetter<>();

String[] str = {"手机","电视","洗衣机"};

for(int i = 0; i < str.length; i ++ ) {

strProductGetter.addProduct(str[i]);

}

String strProduct = strProductGetter.getProduct();

System.out.println("恭喜您抽中了" + strProduct);

System.out.println("=============================================");

// 抽现金

ProductGetter intProductGetter = new ProductGetter<>();

Integer[] integer = {1000,2000,3000};

for(int i = 0; i < integer.length; i ++ ) {

intProductGetter.addProduct(integer[juZKWi]);

}

int intProduct = intProductGetter.getProduct();

System.out.println("恭喜您抽中了" + intProduct);

}

}

// 抽奖器

class ProductGetter{

Random random = new Random();

// 奖品池

ArrayList list = new ArrayList<>();

// 添加奖品

public void addProduct(T t) {

list.add(t);

}

// 抽奖(获取奖品)

public T getProduct() {

return list.get(random.nextInt(list.size()));

}

}

3.4 泛型类的继承

3.4.1 子类也是泛型类

子类也是泛型类,则泛型要保持一致。

class ChildFirst extends Parent{ ... }

1)指定子类泛型,不指定父类泛型,父类默认为Object 类型

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildFirst extends Parent{

@Override

public Object getValue() {

return super.getValue();

}

}

2)若父类保留原有泛型,与子类泛型不一致,则会编译出错

class ChildFirst extends Parent{

@Override

public E getValue() {

return super.getValue();

}

3)父类泛型与子类保持一致

具体泛型指定是由子类传递到父类当中,所以继承时父类要与子类泛型保持一致(当然都写成E也可以)。

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildFirst extends Parent{

@Override

public T getValue() {

return super.getValue();

}

}

4)调用

public class GenericTest {

public static void main(String[] args) {

ChildFirst childFirst = new ChildFirst<>();

childFirst.setValue("chengyu");

System.out.println(childFirst.getValue());

}

}

5)补充:

子类可以进行泛型扩展,但子类必须有一个泛型与父类一致

public class GenericTest {

public static void main(String[] args) {

ChildFirst childFirst = new ChildFirst<>();

childFirst.setValue("chengyu");

System.out.println(childFirst.getValue());

}

}

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildFirst extends Parent{

@Override

public T getValue() {

return super.getValue();

}

}

3.4.2 子类不是泛型类

子类不是泛型类,父类要明确泛型的数据类型

class ChildSecond extends Parent{ ... }

1)子类不是泛型类,不指定父类泛型,父类默认为Object 类型

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildSecond extends Parent{

@Override

public Object getValue() {

return super.getValue();

}

}

2)父类要明确泛型的数据类型

class ChildSecond extends Parent{

@Override

public String getValue() {

return super.getValue();

}

}

3)调用

public class GenericTest {

public static void main(String[] args) {

ChildSecond childSecond = new ChildSecond();

childSecond.setValue("chengyu2");

System.out.println(childSecond.getValue());

}

}

四、泛型接口

4.1 定义

public interface Generator{ ... }

4.2 使用(与继承特点相同)

4.2.1 实现类不是泛型类

实现类不是泛型类,接口要明确数据类型

class Apple implements Generator{

@Override

public String getKey() {

return "Generator interface";

}

}

4.2.2 实现类也是泛型类

实现类也是泛型类,实现类和接口的泛型类型要一致

class Apple implements Generator{

private T key;

@Override

public T getKey() {

return key;

}

}

五、泛型方法

5.1 定义

5.1.1 泛型方法

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

}

// 泛型方法

public E getProduct(ArrayList list) {

return list.get(random.nextInt(list.size()));

}

5.1.2 静态泛型方法

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

}

// 泛型方法

public E getProduct(ArrayList list) {

return list.get(random.nextInt(list.size()));

}

5.1.3 可变参数的泛型方法

public void print(E... e) {

for(int i = 0; i < e.length;i++){

System.out.println(e[i]);

}

}

// 调用

print(1,2,3,4);

5.2 注意

1)包含泛型列表的方法才是泛型方法,泛型类中使用了泛型的方法并不是泛型方法;

2)泛型列表中声明了泛型类型,才可以在方法中使用泛型类型;

3)泛型方法中的泛型类型独立于泛型类的泛型,与类泛型类型无关;

4)泛型方法可以用static 修饰(泛型类的成员方法不可以)。

5.3 使用

5.3.1 定义泛型方法

// 抽奖器

// @param

class ProductGetter{

Random random = new Random();

// 奖品池

ArrayList list = new ArrayList<>();

// 添加奖品

public void addProduct(T t) {

list.add(t);

}

// 抽奖(获取奖品)

public T getProduct() {

return list.get(random.nextInt(list.size()));

}

// 泛型方法

public E getProduct(ArrayList list) {

return list.get(random.nextInt(list.size()));

}

}

5.3.2 调用泛型方法

public class ProductGetterTest {

public static void main(String[] args) {

ProductGetter intProductGetter = new ProductGetter<>();

ArrayList strList = new ArrayList<>();

strList.add("手机");

strList.add("电视");

strList.add("洗衣机");

String product = intProductGetter.getProduct(strList);

System.out.println("恭喜您抽中了" + product);

}

}

六、类型通配符

6.1 类型通配符介绍

类型通配符一般用【?】代替具体的类型 实参;

6.2 为什么要用类型通配符

泛型类被调用时,需要指定泛型类型,当泛型类的方法被调用时,不能灵活对应多种泛型类型的需求,如下面代码部分所示:

public class BoxTest {

public static void main(String[] args) {

// 3.调用showBox

Box box1 = new Box<>();

box1.setFirst(100);

showBox(box1);

// 4.再次调用showBox

// 出现问题:类型发生变化后会报错

Box box2 = new Box<>();

box2.setFirst(200);

showBox(box2);

}

// 2. 调用泛型类,此时需要指定泛型类型

public stjuZKWatic void showBox(Box box) {

Number first = box.getFirst();

System.out.println(first);

}

}

// 1.定义泛型类

class Box{

private E first;

public E getFirst() {

return first;

}

public void setFirst(E first) {

this.first = first;

}

}

解决上述问题,类型通配符【?】登场

public class BoxTest {

public static void main(String[] args) {

// 3.调用showBox

Box box1 = new Box<>();

box1.setFirst(100);

showBox(box1);

// 4.再次调用showBox

// 问题得以解决

Box box2 = new Box<>();

box2.setFirst(200);

showBox(box2);

}

// 2. 调用泛型类,此时需要指定泛型类型

// 【?】类型通配符等成

public static void showBox(Box> box) {

Object first = box.getFirst();

System.out.println(first);

}

}

// 1.定义泛型类

class Box{

private E first;

public E getFirst() {

return first;

}

public void setFirst(E first) {

this.first = first;

}

}

6.3 泛型通配符上限 extends

【6.2】代码例中,虽然使用了通配符,但 box.getFirst()返回类型仍然需要定义成Object 类型,并不理想。

public static void showBox(Box> box) {

Object first = box.getFirst();

System.out.println(first);

}

通配符上限登场:

调用showBox 方法时,传递的泛型类型可以是Number 及其子类(Integer)

public static void showBox(Box extends Number> box) {

Number first = box.getFirst();

System.out.println(first);

}

注意:

public static void showBox(Box extends Number> box) {

Number first = box.getFirst();

// 此处编译报错:类型不一致

// 虽然定义上限,showBox 被调用时没问题,但在方法内同时定义多种类型,编译器无法识别

Integer second = box.getFirst();

System.out.println(first);

}

6.4 泛型通配符下限 super

public static void showBox(Box super Integer> box) {

Number first = box.getFirst();

System.out.println(first);

}

注意:

遍历时要用Object 类型进行遍历;

七、类型擦除

泛型信息只存在于代码编译阶段,进入JVM之前,与泛型相关的信息会被擦除,这个行为称为类型擦除。

Object o = list.get(i);

String str = (String)o;

System.out.println(str);

}

}

}

输出:

java

Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

at com.chengyu.junit.Test.main(Test.java:18)

二、泛型概念

JDK5 中引入泛型,从而可以在编译时检测是否存在非法的类型数据结构。

其本质就是参数化类型,可以用于类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

public static void main(String[] args) {

ArrayList strList = new ArrayList();

strList.add("java");

strList.add("C#");

for(int i = 0;i < strList.size();i++) {

String str = strList.get(i);

System.out.println(str);

}

}

三、泛型类

3.1 定义与调用

定义类时设置泛型,该泛型可用于类中的属性或方法中,调用该泛型类时,指定具体类型;

// 调用泛型类

public class GenericTest {

public static void main(String[] args) {

Generic strGen = new Generic<>("a");

String key = strGen.getKey();

System.out.println(key);

}

}

// 定义泛型类

// @param 使用类时指定

class Generic{

private T key;

public Generic(T key) {

this.key = key;

}

public T getKey() {

return key;

}

public void setKey(T key) {

this.key = key;

}

@Override

public String toString() {

return "GenericTest [key=" + key + "]";

}

}

3.2 注意

1)调用泛型类时未定义类型,则会按照Object 类型处理;

2)调用时分别指定不同类型,但本质都是Object 类型;

3)泛型不支持基本数据类型;

4)泛型类的成员方法不可以用static 修饰(泛型方法可以)。

3.3 使用

需求:抽奖活动,但抽奖内容没有确定,可能都是现金,也可能都是物品

public class ProductGetterTest {

public static void main(String[] args) {

// 抽物品

ProductGetter strProductGetter = new ProductGetter<>();

String[] str = {"手机","电视","洗衣机"};

for(int i = 0; i < str.length; i ++ ) {

strProductGetter.addProduct(str[i]);

}

String strProduct = strProductGetter.getProduct();

System.out.println("恭喜您抽中了" + strProduct);

System.out.println("=============================================");

// 抽现金

ProductGetter intProductGetter = new ProductGetter<>();

Integer[] integer = {1000,2000,3000};

for(int i = 0; i < integer.length; i ++ ) {

intProductGetter.addProduct(integer[juZKWi]);

}

int intProduct = intProductGetter.getProduct();

System.out.println("恭喜您抽中了" + intProduct);

}

}

// 抽奖器

class ProductGetter{

Random random = new Random();

// 奖品池

ArrayList list = new ArrayList<>();

// 添加奖品

public void addProduct(T t) {

list.add(t);

}

// 抽奖(获取奖品)

public T getProduct() {

return list.get(random.nextInt(list.size()));

}

}

3.4 泛型类的继承

3.4.1 子类也是泛型类

子类也是泛型类,则泛型要保持一致。

class ChildFirst extends Parent{ ... }

1)指定子类泛型,不指定父类泛型,父类默认为Object 类型

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildFirst extends Parent{

@Override

public Object getValue() {

return super.getValue();

}

}

2)若父类保留原有泛型,与子类泛型不一致,则会编译出错

class ChildFirst extends Parent{

@Override

public E getValue() {

return super.getValue();

}

3)父类泛型与子类保持一致

具体泛型指定是由子类传递到父类当中,所以继承时父类要与子类泛型保持一致(当然都写成E也可以)。

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildFirst extends Parent{

@Override

public T getValue() {

return super.getValue();

}

}

4)调用

public class GenericTest {

public static void main(String[] args) {

ChildFirst childFirst = new ChildFirst<>();

childFirst.setValue("chengyu");

System.out.println(childFirst.getValue());

}

}

5)补充:

子类可以进行泛型扩展,但子类必须有一个泛型与父类一致

public class GenericTest {

public static void main(String[] args) {

ChildFirst childFirst = new ChildFirst<>();

childFirst.setValue("chengyu");

System.out.println(childFirst.getValue());

}

}

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildFirst extends Parent{

@Override

public T getValue() {

return super.getValue();

}

}

3.4.2 子类不是泛型类

子类不是泛型类,父类要明确泛型的数据类型

class ChildSecond extends Parent{ ... }

1)子类不是泛型类,不指定父类泛型,父类默认为Object 类型

class Parent{

private E value;

public E getValue() {

return value;

}

public void setValue(E value) {

this.value = value;

}

}

class ChildSecond extends Parent{

@Override

public Object getValue() {

return super.getValue();

}

}

2)父类要明确泛型的数据类型

class ChildSecond extends Parent{

@Override

public String getValue() {

return super.getValue();

}

}

3)调用

public class GenericTest {

public static void main(String[] args) {

ChildSecond childSecond = new ChildSecond();

childSecond.setValue("chengyu2");

System.out.println(childSecond.getValue());

}

}

四、泛型接口

4.1 定义

public interface Generator{ ... }

4.2 使用(与继承特点相同)

4.2.1 实现类不是泛型类

实现类不是泛型类,接口要明确数据类型

class Apple implements Generator{

@Override

public String getKey() {

return "Generator interface";

}

}

4.2.2 实现类也是泛型类

实现类也是泛型类,实现类和接口的泛型类型要一致

class Apple implements Generator{

private T key;

@Override

public T getKey() {

return key;

}

}

五、泛型方法

5.1 定义

5.1.1 泛型方法

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

}

// 泛型方法

public E getProduct(ArrayList list) {

return list.get(random.nextInt(list.size()));

}

5.1.2 静态泛型方法

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

}

// 泛型方法

public E getProduct(ArrayList list) {

return list.get(random.nextInt(list.size()));

}

5.1.3 可变参数的泛型方法

public void print(E... e) {

for(int i = 0; i < e.length;i++){

System.out.println(e[i]);

}

}

// 调用

print(1,2,3,4);

5.2 注意

1)包含泛型列表的方法才是泛型方法,泛型类中使用了泛型的方法并不是泛型方法;

2)泛型列表中声明了泛型类型,才可以在方法中使用泛型类型;

3)泛型方法中的泛型类型独立于泛型类的泛型,与类泛型类型无关;

4)泛型方法可以用static 修饰(泛型类的成员方法不可以)。

5.3 使用

5.3.1 定义泛型方法

// 抽奖器

// @param

class ProductGetter{

Random random = new Random();

// 奖品池

ArrayList list = new ArrayList<>();

// 添加奖品

public void addProduct(T t) {

list.add(t);

}

// 抽奖(获取奖品)

public T getProduct() {

return list.get(random.nextInt(list.size()));

}

// 泛型方法

public E getProduct(ArrayList list) {

return list.get(random.nextInt(list.size()));

}

}

5.3.2 调用泛型方法

public class ProductGetterTest {

public static void main(String[] args) {

ProductGetter intProductGetter = new ProductGetter<>();

ArrayList strList = new ArrayList<>();

strList.add("手机");

strList.add("电视");

strList.add("洗衣机");

String product = intProductGetter.getProduct(strList);

System.out.println("恭喜您抽中了" + product);

}

}

六、类型通配符

6.1 类型通配符介绍

类型通配符一般用【?】代替具体的类型 实参;

6.2 为什么要用类型通配符

泛型类被调用时,需要指定泛型类型,当泛型类的方法被调用时,不能灵活对应多种泛型类型的需求,如下面代码部分所示:

public class BoxTest {

public static void main(String[] args) {

// 3.调用showBox

Box box1 = new Box<>();

box1.setFirst(100);

showBox(box1);

// 4.再次调用showBox

// 出现问题:类型发生变化后会报错

Box box2 = new Box<>();

box2.setFirst(200);

showBox(box2);

}

// 2. 调用泛型类,此时需要指定泛型类型

public stjuZKWatic void showBox(Box box) {

Number first = box.getFirst();

System.out.println(first);

}

}

// 1.定义泛型类

class Box{

private E first;

public E getFirst() {

return first;

}

public void setFirst(E first) {

this.first = first;

}

}

解决上述问题,类型通配符【?】登场

public class BoxTest {

public static void main(String[] args) {

// 3.调用showBox

Box box1 = new Box<>();

box1.setFirst(100);

showBox(box1);

// 4.再次调用showBox

// 问题得以解决

Box box2 = new Box<>();

box2.setFirst(200);

showBox(box2);

}

// 2. 调用泛型类,此时需要指定泛型类型

// 【?】类型通配符等成

public static void showBox(Box> box) {

Object first = box.getFirst();

System.out.println(first);

}

}

// 1.定义泛型类

class Box{

private E first;

public E getFirst() {

return first;

}

public void setFirst(E first) {

this.first = first;

}

}

6.3 泛型通配符上限 extends

【6.2】代码例中,虽然使用了通配符,但 box.getFirst()返回类型仍然需要定义成Object 类型,并不理想。

public static void showBox(Box> box) {

Object first = box.getFirst();

System.out.println(first);

}

通配符上限登场:

调用showBox 方法时,传递的泛型类型可以是Number 及其子类(Integer)

public static void showBox(Box extends Number> box) {

Number first = box.getFirst();

System.out.println(first);

}

注意:

public static void showBox(Box extends Number> box) {

Number first = box.getFirst();

// 此处编译报错:类型不一致

// 虽然定义上限,showBox 被调用时没问题,但在方法内同时定义多种类型,编译器无法识别

Integer second = box.getFirst();

System.out.println(first);

}

6.4 泛型通配符下限 super

public static void showBox(Box super Integer> box) {

Number first = box.getFirst();

System.out.println(first);

}

注意:

遍历时要用Object 类型进行遍历;

七、类型擦除

泛型信息只存在于代码编译阶段,进入JVM之前,与泛型相关的信息会被擦除,这个行为称为类型擦除。


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

上一篇:【网络原理】期末复习笔记 第三章 数据链路层
下一篇:来自51CTOHCNP3期一位技术小白的内心独白
相关文章

 发表评论

评论列表