• java的面向对象基础(6)——高级泛型


    嵌入式之路,贵在日常点滴

                                                                    ---阿杰在线送代码

    目录

    一、泛型简介

    二、泛型类及特点

    1、泛型的类型参数可以是泛型类

    2、泛型类可以同时设置多个类型参数

    3、泛型类可以继承泛型类

    4、泛型类可以实现泛型接口

    三、限制泛型可用类型 

    四、类型通配的方式

    类型通配声明 例子 

    类型通配的方式 

    五、泛型方法 

    什么时候使用泛型方法,而不是泛型类呢?


    问题:如果我们需要产生多个对象,每个对象的逻辑完全一样,只要对象内的成员变量的类型不同,那我们如何去做?

    • 创建多个类文件,给每个类中的成员变量设置指定的数据类型。

            缺点:这种方式会导致类的膨胀,重用型太差。 

    1. class Cls1
    2. {
    3. int a;
    4. public Cls1(int a){
    5. this.a = a;
    6. }
    7. public int getData(){
    8. return a;
    9. }
    10. }
    11. class Cls2
    12. {
    13. String a;
    14. public Cls2(String a){
    15. this.a = a;
    16. }
    17. public String getData(){
    18. return a;
    19. }
    20. }
    21. public class Test {
    22. public static void main(String[] args) {
    23. Cls1 cls1 = new Cls1(10);
    24. System.out.println(cls1.getData());
    25. Cls2 cls2 = new Cls2("ajie");
    26. System.out.println(cls2.getData());
    27. }
    28. }

    运行结果:

    10
    ajie 

    • 创建一个类文件,给这个类中的成员变量设置Object数据类型。

           缺点,编译的时候正常,但运行的时候可能会异常。 (因为其类型比较模糊,有时候做了错误得类型转换就会导致运行程序时崩溃,编译没有错误)

    鼠标定位到数据类型,右键 选择 Type Hierarchy

    观察可以看出Object是所有数据类型的父类

    1. class Cls1
    2. {
    3. Object a;
    4. public Cls1(Object a){
    5. this.a = a;
    6. }
    7. public Object getData(){
    8. return a;
    9. }
    10. }
    11. public class Test {
    12. public static void main(String[] args) {
    13. Cls1 cls1 = new Cls1(10);
    14. System.out.println(cls1.getData());
    15. Cls1 cls2 = new Cls1("ajie");
    16. System.out.println(cls2.getData());
    17. }
    18. }

    为解决此问题,我们引入 泛型

    一、泛型简介

    • 泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的。
    • 泛型的原理就是"类型的参数化",即把类型看作参数。也就是说把所要操作的数据类型看作参数,就像方法的形式参数是运行时传递的值的占位符一样。
    • 简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。
    • 泛型可以提高代码的扩展性和重用性。 
    1. class Cls1
    2. {
    3. T a;
    4. public Cls1(T a){
    5. this.a = a;
    6. }
    7. public T getData(){
    8. return a;
    9. }
    10. }
    11. public class Test {
    12. public static void main(String[] args) {
    13. Cls1 cls1 = new Cls1(10);
    14. System.out.println(cls1.getData());
    15. Cls1 cls2 = new Cls1("ajie");
    16. System.out.println(cls2.getData());
    17. }
    18. }

    运行结果:

    10
    ajie 

    myeclipse工程环境出错:Set project compiler compliance settings to '1.5'

    解决方案 

    二、泛型类及特点

    1、泛型的类型参数可以是泛型类

    1. class Cls1
    2. {
    3. T a;
    4. public Cls1(T a){
    5. this.a = a;
    6. }
    7. public T getData(){
    8. return a;
    9. }
    10. }
    11. public class Test {
    12. public static void main(String[] args) {
    13. Cls1> cls1 = new Cls1>(new Cls1(10));
    14. System.out.println(cls1.getData().getData());
    15. }
    16. }

    运行结果: 

    10

    2、泛型类可以同时设置多个类型参数

    1. class Cls2
    2. {
    3. T a;
    4. T2 b;
    5. public Cls2(T a,T2 b){
    6. this.a = a;
    7. this.b = b;
    8. }
    9. public T getData(){
    10. return a;
    11. }
    12. public T2 getData2(){
    13. return b;
    14. }
    15. }
    16. public class Test {
    17. public static void main(String[] args) {
    18. Cls2 cls3 = new Cls2(100,"ajie");
    19. System.out.println(cls3.getData());
    20. System.out.println(cls3.getData2());
    21. System.out.println(cls3.getData()+cls3.getData2());
    22. Cls2 cls4 = new Cls2(100,10);
    23. System.out.println(cls4.getData()+cls4.getData2());//注意,在以前我们这里用到的+都是链接作用
    24. //在这里+可以作为加法使用
    25. int sum = 0;
    26. sum = cls4.getData()+cls4.getData2();
    27. System.out.println(sum);
    28. }
    29. }

    运行结果:

    100
    ajie
    100ajie
    110
    110 

    3、泛型类可以继承泛型类

    1. abstract class Cls1
    2. {
    3. T a;
    4. public Cls1(T a){
    5. this.a = a;
    6. }
    7. public T getData(){
    8. return a;
    9. }
    10. abstract void printInfo();
    11. }
    12. class Cls2 extends Cls1
    13. {
    14. T2 b;
    15. public Cls2(T a,T2 b){
    16. super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
    17. this.b = b;
    18. }
    19. public T2 getData2(){
    20. return b;
    21. }
    22. @Override
    23. void printInfo() {
    24. // TODO Auto-generated method stub
    25. System.out.println("print");
    26. }
    27. }
    28. public class Test {
    29. public static void main(String[] args) {
    30. Cls2 cls = new Cls2(10,"ajie");
    31. System.out.println(cls.getData());
    32. System.out.println(cls.getData2());
    33. cls.printInfo();
    34. }
    35. }

    运行结果:

    10
    ajie
    print

    4、泛型类可以实现泛型接口

    1. abstract class Cls1
    2. {
    3. T a;
    4. public Cls1(T a){
    5. this.a = a;
    6. }
    7. public T getData(){
    8. return a;
    9. }
    10. abstract void printInfo();
    11. }
    12. interface Cls3
    13. {
    14. abstract void printInfoCls3(T t);
    15. }
    16. class Cls2 extends Cls1 implements Cls3
    17. {//如果Cls2的T和Cls3T是同名,则在下面主函数使用时,Cls3的类型遵循Cls2,否则会报错
    18. //即使不同名,也会报错 问题如何解决 在下面展示
    19. T2 b;
    20. public Cls2(T a,T2 b){
    21. super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
    22. this.b = b;
    23. }
    24. public T2 getData2(){
    25. return b;
    26. }
    27. @Override
    28. void printInfo() {
    29. // TODO Auto-generated method stub
    30. System.out.println("print");
    31. }
    32. @Override
    33. public void printInfoCls3(T t) {
    34. // TODO Auto-generated method stub
    35. System.out.println(t);
    36. }
    37. }
    38. public class Test {
    39. public static void main(String[] args) {
    40. Cls2 cls = new Cls2(10,"ajie");
    41. System.out.println(cls.getData());
    42. System.out.println(cls.getData2());
    43. cls.printInfo();
    44. cls.printInfoCls3(100);
    45. }
    46. }

    运行结果:

    10
    ajie
    print
    100 

    如何解决 如果Cls2的T和Cls3的T是同名,则在下面主函数使用时,Cls3的T类型遵循Cls2,否则会报错 问题。即使不同名,也会报错

    1. abstract class Cls1
    2. {
    3. T a;
    4. public Cls1(T a){
    5. this.a = a;
    6. }
    7. public T getData(){
    8. return a;
    9. }
    10. abstract void printInfo();
    11. }
    12. interface Cls3
    13. {
    14. abstract void printInfoCls3(T3 t);
    15. }
    16. class Cls2 extends Cls1 implements Cls3
    17. {//如果Cls2的T和Cls3T是同名,则在下面主函数使用时,Cls3的类型遵循Cls2,否则会报错
    18. T2 b;
    19. public Cls2(T a,T2 b){
    20. super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
    21. this.b = b;
    22. }
    23. public T2 getData2(){
    24. return b;
    25. }
    26. @Override
    27. void printInfo() {
    28. // TODO Auto-generated method stub
    29. System.out.println("print");
    30. }
    31. @Override
    32. public void printInfoCls3(T3 t) {
    33. // TODO Auto-generated method stub
    34. System.out.println(t);
    35. }
    36. }
    37. public class Test {
    38. public static void main(String[] args) {
    39. Cls2 cls = new Cls2(10,"ajie");
    40. System.out.println(cls.getData());
    41. System.out.println(cls.getData2());
    42. cls.printInfo();
    43. cls.printInfoCls3("ajie");
    44. }
    45. }

    三、限制泛型可用类型 

    在定义泛型类别时,默认在实例化泛型类的时候可以使用任何类型,但是如果想要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口

    当没有指定泛型继承的类型或接口时,默认使用extends Object。所以默认情况下任何类型都可以作为参数传入。 

    1. //默认情况下,以下两种情况是一样的
    2. class Cls1
    3. {
    4. T a;
    5. public Cls1(T a){
    6. this.a = a;
    7. }
    8. public T getData(){
    9. return a;
    10. }
    11. }
    12. class Cls1
    13. {
    14. T a;
    15. public Cls1(T a){
    16. this.a = a;
    17. }
    18. public T getData(){
    19. return a;
    20. }
    21. }
    1. //使用extends关键字指定这个类型
    2. abstract class Cls1
    3. {
    4. T a;
    5. public Cls1(T a){
    6. this.a = a;
    7. }
    8. public T getData(){
    9. return a;
    10. }
    11. abstract void printInfo();
    12. }
    13. interface Cls3
    14. {
    15. abstract void printInfoCls3(T3 t);
    16. }
    17. class Cls2 extends Cls1 implements Cls3
    18. {
    19. T2 b;
    20. public Cls2(T a,T2 b){
    21. super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
    22. this.b = b;
    23. }
    24. public T2 getData2(){
    25. return b;
    26. }
    27. @Override
    28. void printInfo() {
    29. // TODO Auto-generated method stub
    30. System.out.println("print");
    31. }
    32. @Override
    33. public void printInfoCls3(T3 t) {
    34. // TODO Auto-generated method stub
    35. System.out.println(t);
    36. }
    37. }

    1. //使用extends关键字指定这个类型必须是继承某个类
    2. class Animal
    3. {
    4. }
    5. class Dog extends Animal
    6. {
    7. }
    8. abstract class Cls1
    9. {
    10. T a;
    11. public Cls1(T a){
    12. this.a = a;
    13. }
    14. public T getData(){
    15. return a;
    16. }
    17. abstract void printInfo();
    18. }
    19. interface Cls3
    20. {
    21. abstract void printInfoCls3(T3 t);
    22. }
    23. class Cls2 extends Cls1 implements Cls3
    24. {
    25. T2 b;
    26. public Cls2(T a,T2 b){
    27. super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
    28. this.b = b;
    29. }
    30. public T2 getData2(){
    31. return b;
    32. }
    33. @Override
    34. void printInfo() {
    35. // TODO Auto-generated method stub
    36. System.out.println("print");
    37. }
    38. @Override
    39. public void printInfoCls3(T3 t) {
    40. // TODO Auto-generated method stub
    41. System.out.println(t);
    42. }
    43. }

    1. //使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。
    2. interface Move
    3. {
    4. abstract void test();
    5. }
    6. class A implements Move//正常继承实现接口的方法使用的是implements
    7. {
    8. public void test(){
    9. }
    10. }
    11. abstract class Cls1//但是在泛型这里需要使用extends限定
    12. {
    13. T a;
    14. public Cls1(T a){
    15. this.a = a;
    16. }
    17. public T getData(){
    18. return a;
    19. }
    20. abstract void printInfo();
    21. }
    22. interface Cls3
    23. {
    24. abstract void printInfoCls3(T3 t);
    25. }
    26. class Cls2 extends Cls1 implements Cls3
    27. {
    28. T2 b;
    29. public Cls2(T a,T2 b){
    30. super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
    31. this.b = b;
    32. }
    33. public T2 getData2(){
    34. return b;
    35. }
    36. @Override
    37. void printInfo() {
    38. // TODO Auto-generated method stub
    39. System.out.println("print");
    40. }
    41. @Override
    42. public void printInfoCls3(T3 t) {
    43. // TODO Auto-generated method stub
    44. System.out.println(t);
    45. }
    46. }

    以上这些实际编程时很少用,现在是用来科普我们的认知,看到要认识。

    后续编程安卓可能会用到。 

    四、类型通配的方式

    同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,不能相互赋值

    1. Cls1 c1 = new Cls1();
    2. Cls1 c2 = new Cls1();
    3. c1 = c2;//发生编译错误
    4. Cls1 c3 = c1;//c3和c1类型并不兼容,发生编译错误
    5. c3 = c2;//c3和c2类型同样不兼容,也会发生编译错误
    6. 泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型通配符(?)声明泛型类的变量就可以解决这个问题。 

      类型通配声明 例子 

      类型通配的方式 

      • "?"代表任意一个类型 
      1. class Animal
      2. {
      3. }
      4. class Dog extends Animal
      5. {
      6. }
      7. class Cls1
      8. {
      9. T a;
      10. public Cls1(T a){
      11. this.a = a;
      12. }
      13. public T getData(){
      14. return a;
      15. }
      16. }
      17. public class Test {
      18. public static void main(String[] args) {
      19. Cls1 c1 = new Cls1(10);
      20. Cls1 c2 = new Cls1(10.0);
      21. Cls1 c3;
      22. c3 = c1;
      23. c3 = c2;
      24. }
      25. }
      • 和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限 
      1. class Animal
      2. {
      3. }
      4. class Dog extends Animal
      5. {
      6. }
      7. class Cls1
      8. {
      9. T a;
      10. public Cls1(T a){
      11. this.a = a;
      12. }
      13. public T getData(){
      14. return a;
      15. }
      16. }
      17. public class Test {
      18. public static void main(String[] args) {
      19. Cls1 c1 = new Cls1(new Dog());
      20. Cls1 c2;
      21. c2 = c1;
      22. }
      23. }
      • 还可以使用super关键词限定通配符匹配类型的下限
      1. class Animal
      2. {
      3. }
      4. class Dog extends Animal
      5. {
      6. }
      7. class Cls1
      8. {
      9. T a;
      10. public Cls1(T a){
      11. this.a = a;
      12. }
      13. public T getData(){
      14. return a;
      15. }
      16. }
      17. public class Test {
      18. public static void main(String[] args) {
      19. Cls1 c1 = new Cls1(new Dog());
      20. Cls1 c2;
      21. //Cls1 c1 = new Cls1(new Dog());
      22. //Cls1 c2;
      23. c2 = c1;
      24. }
      25. }

      五、泛型方法 

      • 不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:
      1. 访问修饰符 <泛型列表> 返回类型 方法名(参数列表){
      2. 实现代码
      3. }
      • 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。
      • 类中的其他方法不能使用当前方法声明的泛型。

      提示: 是否拥有泛型方法与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。

      1. class A
      2. {
      3. public void printInfo(T t){
      4. System.out.println(t);
      5. }
      6. }
      7. class B
      8. {
      9. public void printInfo(T t){
      10. System.out.println(t);
      11. }
      12. }
      13. public class Test {
      14. public static void main(String[] args) {
      15. A a = new A();
      16. a.printInfo("ajie");//此泛型中的方法参数类型被类的泛型类型限制住
      17. B b = new B();
      18. b.printInfo("ajiezaixian");//泛型方法不应该被类的泛型类型限制住
      19. b.printInfo(1234);
      20. b.printInfo(0.5);
      21. }
      22. }

      运行结果:

      ajie
      ajiezaixian
      1234
      0.5 

      1. class A
      2. {
      3. public void printInfo(T t){
      4. System.out.println(t);
      5. }
      6. }
      7. class B
      8. {
      9. //泛型方法
      10. public void printInfo(T t){
      11. System.out.println(t);
      12. }
      13. //泛型方法的重载
      14. public void printInfo(T t,T2 t2){
      15. System.out.println(t);
      16. System.out.println(t2);
      17. //System.out.println(t+t2);会报错 只有实例化确定类型后才让加 跟我们之前遇到的要区分开
      18. }
      19. }
      20. public class Test {
      21. public static void main(String[] args) {
      22. A a = new A();
      23. a.printInfo("ajie");//此泛型中的方法参数类型被类的泛型类型限制住
      24. B b = new B();
      25. b.printInfo("ajiezaixian");//泛型方法不应该被类的泛型类型限制住
      26. b.printInfo(1234);
      27. b.printInfo(0.5);//比方法重载好用多了
      28. b.printInfo("ajie", "这是泛型方法的重载");
      29. }
      30. }

      运行结果

       ajie
      ajiezaixian
      1234
      0.5
      ajie
      这是泛型方法的重载

      什么时候使用泛型方法,而不是泛型类呢?

      添加类型约束只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。

      施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。 

      1. class Animal
      2. {
      3. public void eat(){
      4. System.out.println("动物吃");
      5. }
      6. }
      7. class Dog extends Animal
      8. {
      9. public void eat(){
      10. System.out.println("啃骨头");
      11. }
      12. }
      13. class Cat extends Animal
      14. {
      15. public void eat(){
      16. System.out.println("吃鱼肉");
      17. }
      18. }
      19. class B
      20. {
      21. //泛型方法
      22. public void printInfo(T t){
      23. System.out.println(t);
      24. }
      25. public static void printInfo(T t){
      26. t.eat();
      27. }
      28. }
      29. public class Test {
      30. public static void main(String[] args) {
      31. B b = new B();
      32. b.printInfo(new Dog());
      33. b.printInfo(new Cat());
      34. b.printInfo(new Animal());
      35. B.printInfo(new Dog());
      36. }
      37. }

      运行结果:

      啃骨头
      吃鱼肉
      动物吃
      啃骨头 

    7. 相关阅读:
      Go并发可视化解释 - sync.WaitGroup
      【js】JavaScript清除所有(多个)定时器的方法:
      电子科大软件系统架构设计——系统规划
      React ts学习重点(组件状态,Hooks,路由)
      分布式消息通信之Kafka的实现原理
      实战SpringMVC之CRUD
      软件性能瓶颈问题之数据库性能问题定位
      来看看火爆全网的ChatGPT机器人写的武侠小说,我直呼内行!
      4-1 张量的结构操作
      使用 Docker 部署 instantbox 轻量级 Linux 系统
    8. 原文地址:https://blog.csdn.net/weixin_50546241/article/details/126891863