Java初识泛型 | 如何通过泛型类/泛型方法获取任意类型的三个数的最大值?

简介: 本文介绍了如何使用Java中的泛型来实现一个可以比较任意数值类型最大值的功能。。


一、引言


初学Java时,同学们基本都会遇到这样一个基础编程题:


实验题目:获取三个整数的最大值。


它的答案非常简单,只需要比较三个int类型的变量即可:


public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 30;
        int temp = (a > b) ? a : b;
        int max = (temp > c) ? temp : c;
        System.out.println("max = " + max);
    }
}


那么问题来了:如果想要比较的对象不仅是整数,而是任意的数值类型(甚至任意的自定义类型),那该怎么办呢?


诚然,我们可以通过方法的重载,编写多个适用于不同数据类型的方法。在需要时,我们可以挨个进行调用。但这个未免还是有些吃力,毕竟光是数值类型就有7种,而这7种类型都是可以比较大小的。


因此,我们可以采用泛型类,将数据类型也作为参数传递,达到比较“任意的数值类型”的效果。


这个方法主要用到了泛型的上界这一知识点。对于泛型的基本语法,便不再赘述,需要的同学可以再自行搜索、熟悉。


二、泛型上界


1、什么是泛型的上界


为什么要引出泛型的上界?因为在实际编程中,在定义泛型类时,有时需要对传入的类型变量做一定的约束。


比如上面这个题目,约束就是“数值类型”,也就是Number类型。Integer、Double等类型(注意:泛型类型是引用类型,要传基本数据类型只能传包装类)都可以进行大小比较,但此时String类型就不在数值类型的范围内。这就是约束,约束传入的数据类型是哪些,而不是随便哪个数据类型都可以传进去。


我们可以通过类型边界来实现这种约束。


2、泛型上界的语法


class 泛型类名称<类型形参 extends 类型边界> {
...
}


例如:

public class Test<E extends Number> {
...
}


<E extends Number>的含义就是,Test类只接受 Number 的子类(或Number类本身)作为 E 。如果没有指定E的上界(PS:泛型没有下届,泛型的边界就是指泛型是上界)。


因此:


Test<Integer> a; // 正确,因为 Integer 是 Number 的子类型
Test<String> b; // 编译错误,因为 String 不是 Number 的子类型


这样就达到了约束的效果。


但是这样还不够。如果要约束的上界不是一个类,而是一个接口,该如何呢?语法如下:


public class Test<E extends Comparable<E>> {
...
}


<E extends Comparable<E>>的含义是,传入的E必须实现了Comparable接口。


三、泛型方法


1、泛型方法的语法


方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { 
    ... 
}


例如:


public class Util {
    //静态的泛型方法 需要在static后用<>声明泛型类型参数
    public static <E> void swap(E[] array, int i, int j) {
        E t = array[i];
        array[i] = array[j];
        array[j] = t;
    }
}


此时,Util不是泛型类,但swap是泛型方法。该方法在 🔗Java初识泛型 | 如何通过泛型类/泛型方法实现求数组元素最大值?一文的拓展部分有所应用。此处我们只需要知道泛型方法的语法即可。


2、泛型方法的类型推导


类型推导是指,在调用泛型方法时,可以不写明方法的类型,而Java会根据传入参数的类型自动推导出泛型方法中的泛型是指那种类型。如我们既可以这样写:


Integer[] a = { ... };
swap(a, 0, 9);
String[] b = { ... };
swap(b, 0, 9);
//没有写明swap传入的类型是什么,而是Java自动推导


也可以这样写:


Integer[] a = { ... };
Util.<Integer>swap(a, 0, 9);
String[] b = { ... };
Util.<String>swap(b, 0, 9);
//写明了swap方法传入的类型


有了以上的知识,就可以进行编程了。


四、编程分析


1、MyCompare<T>泛型类


public class MyCompare<T extends Comparable<T>>{
/*    public T getMax(T x, T y) {
        return x > y ? x : y;   //错误!
    }*/
    public T getMax(T x, T y) {
        return x.compareTo(y) >= 0 ? x : y;
    }
}

由于T本身代表的就是一个引用类型,因此,直接用大于小于号比较是不正确的。

不能写作:



调用compareTo()方法才是正确的比较方式。但compareTo()方法不是可以直接调用的,使用之前必须实现Comparable接口。因此,我们需要对传入的类型进行约束,令传入的类型一定得是实现了Comparable接口的,最后再调用类型的compareTo()方法进行比较。


因此,我们写作:


public class MyCompare<T extends Comparable<T>> {
    //获取较大值的方法
    ...
}


含义为,MyCompare类只能传入实现过Comparable接口的类型T。


然后我们调用:


import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        double a = reader.nextDouble();
        double b = reader.nextDouble();
        double c = reader.nextDouble();
 
        MyCompare<Double> m = new MyCompare<>();
        double temp = m.getMax(a,b);
        double max = m.getMax(temp,c);
        System.out.println("max = " + max);
    }
}


此时传入了Double类型(Double类型是实现了Comparable接口的)。



将Double改为Integer:


import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        int a = reader.nextInt();
        int b = reader.nextInt();
        int c = reader.nextInt();
 
        MyCompare<Integer> m = new MyCompare<>();
        int temp = m.getMax(a,b);
        int max = m.getMax(temp,c);
        System.out.println("max = " + max);
    }
}



甚至可以传入String,因为String也实现了Comparable接口。


import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        String a = reader.next();
        String b = reader.next();
        String c = reader.next();
 
        MyCompare<String> m = new MyCompare<>();
        String temp = m.getMax(a,b);
        String max = m.getMax(temp,c);
        System.out.println("max = " + max);
    }
}



也可以传入自定义的类型Student,需要手动给Student类实现Comparable接口。


1.import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        Student stu1 = new Student(10);
        Student stu2 = new Student(20);
        Student stu3 = new Student(30);
 
        MyCompare<Student> m = new MyCompare<>();
        Student temp = m.getMax(stu1,stu2);
        Student max = m.getMax(temp,stu3);
        System.out.println("max = " + max);
    }
}
 
class Student implements Comparable<Student>{    //实现Comparable接口,接口也要传入泛型Student类
    int age;
    String name;
    double grades;
 
    public Student(int age) {    //构造方法 由于只用到age,就只写了age
        this.age = age;
    }
 
    @Override
    public int compareTo(Student o) {    //重写compareTo方法
        return this.age-o.age;
    }
 
    @Override
    public String toString() {    //重写toString方法,便于打印
        return "Student{" +
                "age=" + age +
                '}';
    }
}



2、泛型方法实现


import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        //创建待比较的对象
        String a = new String("hello");
        String b = new String("Hi");
        String c = new String("nice");
 
        //直接调用泛型方法
        //类型推导
        String temp = Util.getMax(a,b);
        String max = Util.getMax(temp,c);
        System.out.println("max = " + max);
    }
}
 
//泛型方法
class Util {
    public static <T extends Comparable> T getMax(T x,T y) {
        return x.compareTo(y) > 0 ? x : y;
    }
}



五、总结


泛型上界是对传入泛型类的类型变量做一定的约束,可以是约束类型变量必须是另一个类的子类或本身,也可以是约束类型变量必须实现了某一个接口。


泛型类型都是引用类型。要实现比较,传入的泛型类型必须是实现了Comparable接口的。语法为<T extends Comparable<T>>。通过调用引用变量自身的compareTo()方法,A.compareTo(B)这样来比较。


注意泛型的语法,包括泛型类实现接口、泛型方法的语法等。

————————————————


                           版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

                     

原文链接:https://blog.csdn.net/wyd_333/article/details/128568280

相关文章
|
2天前
|
Arthas 存储 监控
性能监控之常见 Java Heap Dump 方法
【6月更文挑战8天】性能监控之常见 Java Heap Dump 方法
23 7
|
2天前
|
Java 开发者
Java 面向对象编程实战:从类定义到对象应用,让你成为高手!
【6月更文挑战第15天】在Java中,掌握面向对象编程至关重要。通过创建`Book`类,展示了属性如`title`和`author`,以及构造方法和getter方法。实例化对象如`book1`和`book2`,并访问其属性。进一步扩展类,添加`pages`和`calculateReadingTime`方法,显示了类的可扩展性。在更大规模的项目中,如电商系统,可以定义`Product`、`User`和`Order`类,利用对象表示实体和它们的交互。实践是精通Java OOP的关键,不断学习和应用以提升技能。
|
2天前
|
Java 开发者
为什么 Java 的类和对象如此重要?看完这篇你就懂了!
【6月更文挑战第15天】在Java中,类和对象是核心概念。类作为蓝图定义数据(如角色的名称和健康值)和行为(如`takeDamage`方法),而对象是类的实例,允许交互(如战士受伤)。通过继承,如`Circle`和`Rectangle`扩展`Shape`类,实现多态性,增强代码灵活性。类和对象促进代码组织、复用和可维护性,是高效软件开发的基础。
|
2天前
|
设计模式 Java
一文掌握 Java 面向对象精髓:从类定义到对象实战
【6月更文挑战第15天】Java面向对象编程初学者指南:类是对象模板,如`Person`类含`name`和`age`属性。创建对象用`new`,如`Person person = new Person()`。访问属性如`person.name=&quot;Alice&quot;`,调用方法如`person.sayHello()`。类能继承,如`Student extends Person`。对象间共享数据可传参或共用引用。多态性允许父类引用调用子类方法。注意对象生命周期和内存管理,避免内存泄漏。通过实践和理解这些基础,提升编程技能。
|
2天前
|
Java
深入 Java 面向对象:类的定义,竟然藏着这么多门道!
【6月更文挑战第15天】Java中的类定义是OOP的基础,它封装属性(如Student的name和age)和行为(如study())。简单的类仅触及表面,而复杂的类可模拟真实世界对象的多样性和交互。类还可通过继承扩展,如Student从Person派生,增加特有属性和行为。接口和抽象类等概念进一步增强了灵活性和可扩展性。类定义的深度和广度是构建高效、可维护代码的关键。
|
2天前
|
Java
别再被 Java 类和对象绕晕了!一篇文章让你秒懂!
【6月更文挑战第15天】Java中的类是对象的模板,定义属性(如姓名、年龄)和行为(如走路、说话)。对象是类的实例,有自己的属性值。例如,`Person`类可创建`Person`对象,赋予属性值并调用方法。同样,`Car`类可创建不同品牌的汽车对象,每个对象能加速或刹车。理解类与对象的关系是Java面向对象编程的基础。
|
2天前
|
Java
Java 小白也能看懂!类和对象详解,轻松入门面向对象编程
【6月更文挑战第15天】面向对象编程对新手可能抽象,但理解Java中的类和对象是关键。类是事物模板,如“汽车”类包含属性(颜色、型号、速度)和行为(启动、加速、刹车)。对象是类的实例,像具体的汽车。通过创建对象并调用其方法,可以模拟现实世界的情景。例如,`Car myCar = new Car();`创建了一个汽车对象,之后可设置属性和调用方法。多练习有助于掌握这一概念。
|
2天前
|
Java
Java 面向对象编程:你真的懂类和对象吗?
【6月更文挑战第15天】Java的面向对象编程聚焦于类与对象。类是共享属性和行为的抽象,如`Person`类含名字和年龄。对象是类的具体实例,如`person1`和`person2`各有独特的属性值。设计类时要考虑扩展性,以便适应变化,如添加“性别”属性。对象管理涉及保持其状态和行为的正确性。继承允许类如`Student`扩展`Person`,增加新特性。理解面向对象不仅是创建和使用,更是关于设计和架构的智慧。
|
2天前
|
Java
震惊!原来 Java 的类是这样定义的,你还在等什么?
【6月更文挑战第15天】Java 中的类是对象的蓝图,定义了其属性(如 `name`, `age`)和行为(构造器、方法)。例如,`Student` 类展示了如何通过构造器初始化属性,并通过方法(`getName`, `getAge`)访问这些属性。类的使用促进代码模块化、可维护性和扩展性,允许创建具有相同特征的多个对象,适应不断变化的需求。掌握类定义是提升Java编程能力的基础。
|
3天前
|
Java 编译器
[笔记] 疯狂JAVA讲义(第3版)第9章 泛型
[笔记] 疯狂JAVA讲义(第3版)第9章 泛型