一文学懂java泛型

简介: 1.什么是java泛型Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?答案是可以使用 Java 泛型。使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。


1.什么是java泛型


Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。


泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。


假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?


答案是可以使用 Java 泛型。


使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。


2.泛型的快速入门


代码示例:


import java.util.ArrayList;
/**
 * java泛型
 */
public class Generics {
    public static void main(String[] args) {
        // 使用java泛型对数据类型做限制
        // 限制添加的类型必须为Dog
        ArrayList<Dog> arrayList = new ArrayList<Dog>();
        arrayList.add(new Dog("康康",12));
        arrayList.add(new Dog("旺旺",6));
        System.out.println(arrayList);
    }
}
class Dog {
    private String name;
    private int age;
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


使用泛型,可以直接对于数据进行遍历,无需进行额外的类型强转,这增大了代码的效率:🎶


for (Dog dog : arrayList) {
    System.out.println(dog.getName());
    System.out.println(dog.getAge());
}


3.拥有泛型特性的类


我们来创建一个类:

该类的一个属性为泛型,可以是任意的数据类型,在编译期间,确定E是什么类型


// 泛型类
class Person<E> {
    E s; // 在编译期间,确定E是什么类型
    public Person(E s) { // E可以是参数类型
        this.s = s;
    }
    public E f() { // E也可以是返回类型
        return s;
    }
  @Override
    public String toString() {
        return "Person{" +
                "s=" + s +
                '}';
    }
}


那么我们在创建对象的时候,就可以传入不同的数据类型的数据,这很方便:😋

例如:


Person<String> stringPerson = new Person<String>("www");
System.out.println(stringPerson);  // Person{s=www}
Person<Integer> integerPerson = new Person<Integer>(521);
System.out.println(integerPerson);  // Person{s=521}


4.泛型的使用细节


泛型的<>里面只能是引用类型,不可以为基本数据类型🤪


在给泛型指定具体类型后,可以传入该类型或者该类型的子类型


在实际的开发中,我们一般采用简写的泛型语法(去掉后面<>里面的内容),例如:


Person<Double> doublePerson = new Person<>(13.14);


如果不给泛型指定具体的数据类型,默认为Object类型✨


5.自定义泛型类


泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。


和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。


使用泛型的数组,不能初始化

静态成员不能使用泛型,因为在类加载时,对象还没有创建,JVM无法完成其的初始化操作

接下来,我们看一个自定义泛型类的例子:


/**
 * 自定义泛型类
 */
class Tiger<T,R,M> {
    String name;
    R r;
    M m;
    T t;
    public Tiger(String name, R r, M m, T t) {
        this.name = name;
        this.r = r;
        this.m = m;
        this.t = t;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public R getR() {
        return r;
    }
    public void setR(R r) {
        this.r = r;
    }
    public M getM() {
        return m;
    }
    public void setM(M m) {
        this.m = m;
    }
    public T getT() {
        return t;
    }
    public void setT(T t) {
        this.t = t;
    }
}


6.自定义泛型接口


在接口中,静态成员也不能使用泛型

泛型接口的类型,在继承接口或者实现接口的时候确定

没有指定类型,默认依然为Object类型

自定义泛型接口的例子:


/**
 * 自定义泛型接口
 */
interface IUsb<U, R> {
    R get(U u);
    void hi(R r);
    void run(R r1, R r2, U u1, U u2);
    default R method(U u) {
        return null;
    }
}


这个自定义泛型的接口,如果有接口想要继承他,可以在继承接口时指定泛型接口的类型:


/**
 * 继承接口时指定泛型接口的类型
 */
interface IA extends IUsb<String,Double>{
}


现在当我们实现IA接口时,会用String和Double类型替换我们接口原有的U和R类型:

例如:


/**
 * 实现自定义的泛型接口
 */
class TTT implements IA {
    @Override
    public Double get(String s) {
        return null;
    }
    @Override
    public void hi(Double aDouble) {
    }
    @Override
    public void run(Double r1, Double r2, String u1, String u2) {
    }
}


当然,我们也可以在实现接口的同时指定接口的数据类型,例如:


/**
 * 实现IUsb接口
 */
class III implements IUsb<Integer,String> {
    @Override
    public String get(Integer integer) {
        return null;
    }
    @Override
    public void hi(String s) {
    }
    @Override
    public void run(String r1, String r2, Integer u1, Integer u2) {
    }
}


7.自定义泛型方法


泛型方法,可以定义在普通类中,也可以定义在泛型类中

泛型方法在调用时,类型就确定了

使用代码示例:


public class AdvancedGenerics {
    public static void main(String[] args) {
        fly("保密",521);
        fly(12,true);
    }
    /**
     * 自定义泛型方法
     */
    static public <T, R> void fly(T t, R r) {
        System.out.println(t);
        System.out.println(r);
    }
}
---------------------------
输出:
保密
521
12
true


8.泛型通配符


泛型不具备继承性

类型通配符一般是使用 ? 代替具体的类型参数。例如 List<?> 在逻辑上是List<String>,List<Integer>等所有 List<具体类型实参> 的父类


/**
 * 任意泛型类型
 */
public static void printCollection(List<?> c){
    for (Object o : c) {
        System.out.println(o);
    }
}


<? extends T>表示该通配符所代表的类型是T类型的子类。

<? super T>表示该通配符所代表的类型是T类型的父类。


/**
 * ? extends 表示上限,如下代表可以接受AA或者AA的子类
 */
public static void printCollection2(List<? extends AA>c){
    for (AA aa : c) {
        System.out.println(aa);
    }
}
/**
 * ? super 表示下限,如下表示支持AA类以及AA类的父类,不限于直接父类
 */
public static void printCollection3(List<? super AA>c) {
    for (Object o : c) {
        System.out.println(o);
    }
}


知识给人重量,成就给人光彩,大多数人只是看到了光彩,而不去称量重量。🍳

目录
相关文章
|
5月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
113 2
|
3月前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
90 0
[Java]泛型
|
3月前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
26 1
|
4月前
|
Java 编译器 容器
Java——包装类和泛型
包装类是Java中一种特殊类,用于将基本数据类型(如 `int`、`double`、`char` 等)封装成对象。这样做可以利用对象的特性和方法。Java 提供了八种基本数据类型的包装类:`Integer` (`int`)、`Double` (`double`)、`Byte` (`byte`)、`Short` (`short`)、`Long` (`long`)、`Float` (`float`)、`Character` (`char`) 和 `Boolean` (`boolean`)。包装类可以通过 `valueOf()` 方法或自动装箱/拆箱机制创建。
55 9
Java——包装类和泛型
|
3月前
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
38 5
|
3月前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
29 1
|
3月前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
29 2
|
4月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
|
4月前
|
存储 安全 搜索推荐
Java中的泛型
【9月更文挑战第15天】在 Java 中,泛型是一种编译时类型检查机制,通过使用类型参数提升代码的安全性和重用性。其主要作用包括类型安全,避免运行时类型转换错误,以及代码重用,允许编写通用逻辑。泛型通过尖括号 `&lt;&gt;` 定义类型参数,并支持上界和下界限定,以及无界和有界通配符。使用泛型需注意类型擦除、无法创建泛型数组及基本数据类型的限制。泛型显著提高了代码的安全性和灵活性。
|
3月前
|
安全 Java 编译器
Java基础-泛型机制
Java基础-泛型机制
37 0