java泛型

if年少有为      2022-05-11     553

关键词:

1、什么是泛型

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

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

2、泛型的使用

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

2.1、泛型类

泛型类型用于类的定义中,被称为泛型类。

通过泛型可以完成对一组类对外开放相同的接口。最典型的就是各种容器类,比如:List、Set、Map。

一个普通的泛型类:

/**
 * @param <T>
 * @author Arley
 * date: 2020/02/09 night 20:56
 * desc: 泛型类
 */
public class Generic<T> {

    /* key这个成员变量的类型为T,T的类型由外部指定 */
    private T key;

    public Generic(){

    }

    /* 泛型构造方法形参key的类型也为T,T的类型由外部指定 */
    public Generic(T key) {
        this.key = key;
    }

    /* 泛型方法getKey的返回值类型为T,T的类型由外部指定 */
    public T getKey() {
        return key;
    }
}

定义的泛型类,并不以一定非得传入泛型类型实参!

如果在使用泛型的时候传入泛型实参,则会根据传入的泛型实参做出限制。

如果没有传入泛型实参,在泛型类中用泛型的方法或成员变量定义的类型可以为任何类型。

下面我们看一个栗子:

Generic generic1 = new Generic(666666);
Generic generic2 = new Generic("666666");
Generic generic3 = new Generic(666666L);
Generic generic4 = new Generic(true);
2.2、泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,下面是一个简单的泛型接口:

/**
 * @param <T>
 * @author Arley
 * desc:泛型接口
 */
public interface Generator<T> {
    T method();
}

当实现泛型接口的类,未传入泛型实参时候:

/**
 * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
 * 即:class FruitGenerator<T> implements Generator<T>{
 * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,
 * 编译器会报错:"Unknown class"
 */
public class FruitGenerator<T> implements Generator<T> {
    @Override
    public T method() {
        return null;
    }
}

当实现泛型接口的类,传入泛型实参的时候:

/**
 * 传入泛型实参时:
 * 定义一个类实现这个接口,虽然我们只创建了一个泛型接口 Generator<T>
 * 但是我们可以为 T 传入无数个参数类型,形成了无数个参数的 Generator 接口
 * 在实现类实现泛型接口时,如果将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
 *  即:Generator<String>, public T method()中的 T 也要替换成 String类型
 */
class FruitGenerator2<T> implements Generator<String> {
    @Override
    public String method() {
        return "good";
    }
}
2.3、泛型方法

在 Java 中,泛型类的定义十分简单,但是泛型方法就有点复杂了。

尤其是我们看到大多数的泛型类中的成员方法也都使用了泛型,有的甚至在泛型类中包含泛型方法。

泛型类:是指在实例化类的时候指定泛型的具体类型;

泛型方法:实在调用方法的时候指明泛型的具体类型;

泛型方法介绍:

    /**
     * @param Clazz 传入的泛型实参
     * @return 返回值为 T 类型
     * @throws InstantiationException
     * @throws IllegalAccessException
     * desc:
     *  1): public 与返回值中间 <T> 非常重要,可以理解为声明此方法为泛型方法
     *  2): 只有声明了 <T> 的方法才是泛型方法,泛型类中使用了泛型的成员方法不是泛型方法
     *  3): <T> 表明该方法将使用泛型类型 T,此时才可以在泛型方法中使用 T
     *  4): 与泛型类的定义一样,此处 T 可以随便写为任何标识
     */
public <T> T genericMethod(Class<T> Clazz){
    T  instance = null;
    try {
        instance = Clazz.newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return instance;
}
2.3.1、泛型类中的泛型方法

泛型方法可以出现在任何地方和任何场景中使用。

但是有一种情况特别特殊,当泛型方法出现在泛型类中的时候,我们再来看一下:

/**
 * @author Arley
 * desc:泛型类中的泛型方法
 */
@SuppressWarnings("all")
class Generic2<T> {

    /**
     * 这里的 T 只能是泛型类实参或者实参的子类
     * @param t
     */
    public void test1(T t) {
        System.out.println(t.toString());
    }

    /**
     * 在泛型类中使用泛型方法,使用泛型 <E>,这种泛型可以为任意类型,可以与泛型类中的声明 T 不是同一种类型
     * 由于泛型方法在声明的时候会声明泛型 <E>,因此即使在泛型类中并未声明泛型,编译器也会正确识别泛型方法中     * 的类型
     *
     * @param t
     * @param <E>
     */
    public <E> void test2(E t) {
        System.out.println(t.toString());
    }

    /**
     * 在泛型类中声明一个泛型方法,使用泛型 <T>
     *     注意这个泛型 <T> 是一个全新的类型,可以与泛型类中声明的 <T> 不是一个类型
     * @param t
     * @param <T>
     */
    public <T> void test3(T t) {
        System.out.println(t.toString());
    }

    public static void main(String[] args) {
        Fruit fruit = new Fruit();
        Apple apple = new Apple();
        Person person = new Person();

        Generic2<Fruit> generic2 = new Generic2<>();
        
        /* apple是 Fruit 的子类,这里编译通过*/
        generic2.test1(fruit);
        generic2.test1(apple);

        /* 泛型指定实参为 Fruit,这里传入 Person 编译不通过*/
        //generic2.test1(person);

        /* 使用这个方法两个都可编译通过 */
        generic2.test2(apple);
        generic2.test2(person);

        /* 使用这个方法两个都可编译通过 */
        generic2.test3(apple);
        generic2.test3(person);
    }
}
2.3.2、泛型方法和可变参数
    /**
     * 泛型方法
     * @param args 参数可变
     */
    public <T> void pringMsg(T... args){
        for (T arg : args) {
            System.out.print(arg);
        }
    }
pringMsg("11","22","33");
pringMsg(11,22,33);
pringMsg(1.1,2.2,3.3);
2.3.3、静态方法与泛型

静态方法有一种情况需要注意,那就是在类中的静态方法上使用泛型:

静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

即:如果静态方法要使用泛型,必须将静态方法定义为泛型方法。

/**
 * @author Arley
 * date:2020/02/10 forenoon 10:54
 * desc:静态方法泛型
 */
@SuppressWarnings("all")
public class StaticGenerator<T> {

    /**
     * 如果在类中定义使用泛型的静态方法,需要将这个方法定义为泛型方法
     * 即使静态方法中要使用泛型类中已经声明过的泛型也不可以
     * 如:
     *      public static void test(T t)
     *      此时会编译错误!
     * @param t
     * @param <T>
     */
    public static <T> void test(T t) {
        System.out.println(t.toString());
    }
}
2.4、泛型上下边界通配符

在使用泛型的时候,我们可以为传入的泛型类型实参进行上下文边界的限制。

如:类型实参只允许传入某种类型的父类或者某种类型的子类。

2.4.1、上边界通配符

利用 ? extends Number 形式的通配符,可以实现泛型的向上转型。

  • 为泛型添加上边界通配符,即传入的类型实参必须是指定的类型的子类型。
/**
 * @author Arley
 * desc:泛型的上边界通配符
 */
@SuppressWarnings("all")
class Generic3<T> {

    private T key;

    public Generic3() {

    }

    public Generic3(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }

    /**
     * 为泛型方法添加上边界通配符,传入实参必须是Number的子类
     * @param obj
     */
    public static void test(Generic3<? extends Number> obj) {
        System.out.println(obj.getKey());
    }

    public static void main(String[] args) {
        Generic3<String> generic1 = new Generic3("11");
        Generic3<Integer> generic2 = new Generic3(11);
        Generic3<Long> generic3 = new Generic3(11L);
        Generic3<Float> generic4 = new Generic3(11F);
        Generic3<Double> generic5 = new Generic3(1.1);

        /* 因为 String 不是 Number 的子类,这行代码编译错误*/
        //test(generic1);
        test(generic2);
        test(generic3);
        test(generic4);
        test(generic5);
    }
}

如果我们将泛型类修改一下:

class Generic3<T extends Number> 
/* 这一行代码也会直接报错 */
Generic3<String> generic1 = new Generic3("11");

声明上边界通配符需要注意的地方:

    /**
     * 在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛     * 型声明的时候添加
     * 如:
     *      public <T> T showKeyName(Generic<T extends Number> container)
     *      这样编译会报错
     */
    public <T extends Number> T test2(Generic3<T> t){
        return t.getKey();
    }
2.4.2、下边界通配符

通配符的另一个方向是 “超类型的通配符“ ? super TT 是类型参数的下界。使用这种形式的通配符,我们就可以 ”传递对象” 了

  • 为泛型添加下边界通配符,即传入的类型实参必须是指定的类型的父类型,直至Object。
/**
 * @author Arley
 * desc:泛型的下边界通配符
 */
@SuppressWarnings("all")
class Generic4<T> {

    private T key;

    public Generic4() {

    }

    public Generic4(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }

    /**
     * 为泛型方法添加下边界通配符,传入实参必须是Apple的父类,直至Object
     * @param obj
     */
    public static void test(Generic4<? super Apple> obj) {
        System.out.println(obj.getKey());
    }

    public static void main(String[] args) {
        Object obj = new Object();
        Fruit fruit = new Fruit();
        Apple apple = new Apple();
        Person person = new Person();
        Generic4<Object> generic1 = new Generic4<>(obj);
        Generic4<Fruit> generic2 = new Generic4(fruit);
        Generic4<Apple> generic3 = new Generic4(apple);
        Generic4<Person> generic4 = new Generic4(person);
        
        /* Object Fruit 是Apple父类*/
        test(generic1);
        test(generic2);
        test(generic3);

        /* Person 不是 Apple 的父类,编译不通过*/
        //test(generic4);
    }

项目地址:https://github.com/CrazyArley/Java-Learning

java_泛型笔记(代码片段)

文章目录Java_泛型简介Java_泛型的概念Java_为什么存在泛型Java_自定义泛型结构Java_泛型的声明Java_泛型的实例化Java_泛型类Java_泛型方法Java_泛型在继承上的体现Java_通配符的使用Java_泛型简介集合容器类在设计阶段/声明阶段不能确... 查看详情

java泛型泛型简介(泛型类|泛型方法|静态方法的泛型|泛型类与泛型方法完整示例)(代码片段)

文章目录一、泛型简介二、泛型类三、泛型方法四、静态方法的泛型五、泛型类与泛型方法完整示例一、泛型简介泛型可以简单理解为参数化类型,主要作用在类,方法,接口上;java泛型与C++模板:Java中的泛型,是仿照C++中... 查看详情

java泛型与非泛型java(代码片段)

查看详情

java高级--java泛型

类型的参数化泛型类可以同时设置多个参数泛型类可以继承泛型类泛型类可以实现泛型接口示例--泛型类packagecom.date;publicclassGenericDemo{publicstaticvoidmain(String[]args){ GenClass<String>genClass=newGenClass<String>("tanlei"); System.out 查看详情

java泛型

1、什么是泛型Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许开发人员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。2、... 查看详情

java之泛型

1、为什么要有泛型Generic?2、总结Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不... 查看详情

java泛型是什么?一文带你吃透泛型(代码片段)

文章目录1.Java泛型2.泛型类3.泛型接口4.泛型方法5.泛型集合Java编程基础教程系列1.Java泛型Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。其主要的形式有泛型类,泛型接口和泛型方法。泛型概... 查看详情

java--泛型

泛型简而言之就是类型的参数化泛型分为泛型接口、泛型类和泛型方法,不存在泛型数组。举例//泛型类;publicclassData<T>{privateTdata;publicData(Tdata){this.data=data;}publicTgetData(){returndata;}}publicclassTest02{publicstaticvoidmain(String[]arg 查看详情

java泛型梳理

java泛型梳理概述泛型,即参数化类型,是在JDK1.5之后才开始引入的。所谓参数化类型是指所操作的数据类型在定义时被定义为一个参数,然后在使用时传入具体的类型。这种参数类型可以用在类,接口,方法的创建中,分别被... 查看详情

java泛型

Java泛型Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。假定我们有这... 查看详情

java----泛型

 自定义泛型:浮动  泛型只会在 查看详情

java-28java泛型

Java泛型如果我们只写一个排序方法,就能够对整型数组、字符串数组甚至支持排序的任何类型的数组进行排序,这该多好啊。Java泛型方法和泛型类支持程序员使用一个方法指定一组相关方法,或者使用一个类指定一组相关的类... 查看详情

java泛型

Java泛型如果我们只写一个排序方法,就能够对整形数组、字符串数组甚至支持排序的任何类型的数组进行排序,这该多好啊。Java泛型方法和泛型类支持程序员使用一个方法指定一组相关方法,或者使用一个类指定一组相关的类... 查看详情

java泛型

1/**2*泛型定义3*泛型是JavaSE1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。Java语言引入泛型的好... 查看详情

java学习----泛型

目录  1.泛型的定义  2.泛型的使用  3.泛型通配符 一、泛型的定义  泛型是java5的新特性,使用泛型会让程序编写简单安全,在编译的时候就会检查类型是否安全  定义:所谓泛型就是允许在定义类、接口和方... 查看详情

java泛型

Java中的泛型,通常使用<E>来表示。泛型是指参数化类型。例如:  List<String>li=newArrayList<String>() 泛型是JavaSE1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类... 查看详情

java泛型java使用泛型的意义

Java泛型Java使用泛型的意义@authorixenos  直接意义在编译时保证类型安全 根本意义a)类型安全问题源自可复用性代码的设计,泛型保证了类型安全的复用模板b)使用复用性模板时不用手动强制类型转换 三种泛型实现... 查看详情

java泛型

Java中的泛型(GenericType)基本精神:数据类型参数化集合框架中没使用泛型和使用泛型的比较没使用泛型ListmyIntList=newLinkedList();//1myIntList.add(newInteger(100));//2Integerx=(Integer)myIntList.iterator().next();//3  使用泛型List<Inte 查看详情