java基础知识【第14期】-- 泛型

导读:

本篇是JAVA基础系列的第14篇,今天我们梳理java中的泛型机制。Java泛型这个特性是从JDK 1.5才开始加入的,引入泛型的意义在于:适用于多种数据类型执行相同的代码(代码复用)。

1.泛型概述

Java从JDK 1.5 开始提供了泛型。泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。

泛型本质上是提供类型的“类型参数”,也就是参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。我们可以为类、接口或方法指定一个类型参数,通过这个参数限制操作的数据类型,从而保证类型转换的绝对安全。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

示例代码:

 private static int jia(int a, int b) {
     System.out.println(a + "+" + b + "=" + (a + b));
     return a + b;
 }
 private static float jia(float a, float b) {
     System.out.println(a + "+" + b + "=" + (a + b));
     return a + b;
 }
 private static double jia(double a, double b) {
     System.out.println(a + "+" + b + "=" + (a + b));
     return a + b;
 }

两个数字相加,如果没有泛型,要实现不同类型的加法,每种类型都需要重载一个add方法。上边3个加法只声明了参数类型一样的情况,还有参数类型不同的情况存在。

而通过泛型,我们可以直接定义为一个方法:

 private static <T extends Number> double jia(T a, T b) {
     System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
     return a.doubleValue() + b.doubleValue();
 }

泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)

2.泛型类

泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法。

泛型除了可以定义泛型集合之外,还可以直接限定泛型类的类型参数。语法格式如下:

 public class class_name<data_type1,data_type2,…>{}

其中,class_name 表示类的名称,data_ type1 等表示类型参数。Java 泛型支持声明一个以上的类型参数,只需要将类型用逗号隔开即可。

泛型类一般用于类中的属性类型不确定的情况下。在声明属性时,使用下面的语句:

 private data_type1 property_name1;
 private data_type2 property_name2;

该语句中的 data_type1 与类声明中的 data_type1 表示的是同一种数据类型。

在实例化泛型类时,需要指明泛型类中的类型参数,并赋予泛型类属性相应类型的值。例如,下面的示例代码创建了一个表示学生的泛型类,该类中包括 3 个属性,分别是姓名、年龄和性别。

 public class Stu<N, A, S> {
     private N name; // 姓名
     private A age; // 年龄
     private S sex; // 性别
     // 创建类的构造函数
     public Stu(N name, A age, S sex) {
         this.name = name;
         this.age = age;
         this.sex = sex;
    }
     // 下面是上面3个属性的setter/getter方法
     public N getName() {
         return name;
    }
     public void setName(N name) {
         this.name = name;
    }
     public A getAge() {
         return age;
    }
     public void setAge(A age) {
         this.age = age;
    }
     public S getSex() {
         return sex;
    }
     public void setSex(S sex) {
         this.sex = sex;
    }
 }

接着创建测试类。在测试类中调用 Stu 类的构造方法实例化 Stu 对象,并给该类中的 3 个属性赋予初始值,最终需要输出学生信息。测试类的代码实现如下:

 public class Demo {
     public static void main(String[] args) {
         Stu<String, Integer, Character> stu = new Stu<String, Integer, Character>("张三", 18, '男');
         String name = stu.getName();
         Integer age = stu.getAge();
         Character sex = stu.getSex();
         System.out.println("学生信息如下:");
         System.out.println("学生姓名:" + name + ",年龄:" + age + ",性别:" + sex);
    }
 }

在该程序的 Stu 类中,定义了 3 个类型参数,分别使用 N、A 和 S 来代替,同时实现了这 3 个属性的 setter/getter 方法。在主类中,调用 Stu 类的构造函数创建了 Stu 类的对象,同时指定 3 个类型参数,分别为 String、Integer 和 Character。在获取学生姓名、年龄和性别时,不需要类型转换,程序隐式地将 Object 类型的数据转换为相应的数据类型。

3.泛型方法

泛型同样可以在类中包含参数化的方法,而方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是不是泛型没有关系。

泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。另外,对一个 static 的方法而言,无法访问泛型类的类型参数。因此,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。

定义泛型方法的语法格式如下:

 [访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])

例如:

 public static <T> List find(Class<T> cs,int userId){}

一般来说编写 Java 泛型方法,其返回值类型至少有一个参数类型应该是泛型,而且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,那么这个泛型方法的使用就被限制了。

示例代码:

public class Demo {
    public static <T> void List(T book) { // 定义泛型方法
        if (book != null) {
            System.out.println(book);
        }
    }
    public static void main(String[] args) {
        Book book = new Book(1, "Java面向对象");
        List(book); // 调用泛型方法
    }
}

说明一下,定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。

4.泛型接口

泛型除了可以被定义在方法和类上,接口同样适用

定义语法:

interface interface1<T>{}
interface SubClass<T> implements Interface1<T>{}

Java接口的泛型,常用一共就两种!

  • 第一种在实现接口的子类依旧使用泛型,在实例化的时候在动态添加type

  • 第二种形式,子类实现接口的时候具体化type,在实例化的时候不用再动态添加type

interface Message<T>{
    public void demo(T t);
}
//第一种在实现接口的子类依旧使用泛型,在实例化的时候在动态添加type
class MessageOne<T> implements Message<T>{ 
    public void demo(T t){
        System.out.println(t.toString());
    }
}
//第二种形式,子类实现接口的时候具体化type,在实例化的时候不用再动态添加type
class MessageTwo implements Message<String>{
    public void demo(String t){
        System.out.println(t.toString());
    }
}

5.泛型的上下限

在 Java 中默认可以使用任何类型来实例化一个泛型类对象。当然也可以对泛型类实例的类型进行限制,在使用泛型的时候,我们可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

语法格式如下:

class 类名称<T extends anyClass> // 上限
class 类名称<T super anyClass>  // 下限

上限

public class Num<T extends Number> {
    // 定义泛型变量
    private T var;        

    public void setVar(T var) {
        this.var = var;
    }

    public T getVar() {
        return this.var;
    }
	// 直接打印
    public String toString() {    
        return this.var.toString();
    }

    public static void main(String args[]) {
        // 声明Integer的泛型对象
        Num<Integer> num = new Num<Integer>();
        num.setVar(10);
        System.out.println(num);
    }
}

下限

 public class Info<T> {
     private T var;        // 定义泛型变量
     public void setVar(T var) {
         this.var = var;
    }
     public T getVar() {
         return this.var;
    }
     public String toString() {    // 直接打印
         return this.var.toString();
    }
 }
 
 class Demo {
     public static void main(String args[]) {
         // 声明String的泛型对象
         Info<String> info1 = new Info<String>();  
         // 声明Object的泛型对象
         Info<Object> info2 = new Info<Object>();      
         info1.setVar("hello");
         info2.setVar(new Object());
         fun(info1);
         fun(info2);
    }
     // 只能接收String或Object类型的泛型,String类的父类只有Object类
     public static void fun(Info<? super String> temp) {
         System.out.print(temp + ", ");
    }
 }

泛型总结

  • <?> 无限制通配符

  • <? extends E> extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类

  • <? super E> super 关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值