Java泛型

目录

泛型在Java中是语法糖

泛型使用

泛型类

占位标识

泛型接口

泛型方法

通配符

类型通配符

带限通配符

上限通配符extend 取数据

下限通配符super 添数据

泛型擦除


泛型在Java中是语法糖

        语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。Java中最常用的语法糖主要有泛型、可变参数、条件编译、自动拆装箱、内部类等。虚拟机并不支持这些语法,它们在编译阶段就被还原回了简单的基础语法结构,这个过程成为解语法糖。        

        Java泛型的主要目的就是为了便于开发者写代码时限制传入参数的类型,在编译时期就进行提示强制设置传入的参数类型。用于避免绝大多数的类型转换异常。

泛型使用

泛型类

public class Generics<T> {
    private T var;

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

    public T getVar(){
        return var;
    }

    public static void main(String[] args) {
        // 在声明前就指定需要传入的类型 不声明类型就默认示Object
        Generics generics = new Generics();
        // 指定传入的类型必须是引用数据类型
        Generics<String> genericsTwo= new Generics<>();
        genericsTwo.setVar("aaa");
    }
}

占位标识

        看了上面的栗子可能比较懵 怎么就多了个<T>呢 其实我们用<>来标识这是一个泛型类 写在类名后面。而T则是占位标识。这个占位标识可以随意取名只要符合命名规范即可。

比如

class A<sdadad>{
    private sdadad var;
}

但是见明知意嘛,我们也不能太随意 一般我们常用的表示方式有

        T Type

        K V Key Value

        E Element

        V ViewBinding M ViewModel

注意传入的参数只能引用数据类型

基本数据类型是不能作为指定的泛型参数的

泛型接口

interface  Comparator<T>{
    void compare(T t);
    T compare();
}

泛型方法

在方法的返回值前指定标识位

class Method{
    public <T> void test(T t){
        System.out.println(t.getClass().toString());
    }

    public static void main(String[] args) {
        Method method = new Method();
        method.test("有什么意义呢?对比直接使用Object。答:没什么意义");
    }
}

显然当我们不涉及类型操作时,泛型有什么意义呢?没有意义!

如何才显得泛型方法有意义呢?使用泛型可以避免类型转换的问题。先来一个小小的对比。

但是下面的通配符对传入的参数进行限制进行才是泛型受人喜爱的原因。

class Method{

    // 这样才有一点意义
    public <T>  T test2(T t){
        return t;
    }

    public Object test3(Object object){
        return object;
    }

 
    public static void main(String[] args) {
        Method method = new Method();
        // 使用泛型可以避免类型转换的问题
        String str = method.test2("ssss");
        String strTwo = (String)method.test3("sdadas");
    }

    // 泛型支持可变参数
    public <T> void test(T... t){}

    // 或者限定对传入参数的类型进行限制才有意义
}

通配符

类型通配符

顾名思义就是匹配任意类型的类型实参。

? 来表示<?>

简单讲就是传什么都行

 class WildCard<T>{
        private T var;

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

        public T getVar(){
            return var;
        }

        public static void main(String[] args) {
            WildCard<?> wildCard = new WildCard<>();
            WildCard<Object> wildCard1 = new WildCard<>();
            // 有什么用呢?对比于Object 显然这样使用和Object没两样
            // 怎么才有用呢 配合 extends/super
        }
    }

带限通配符

但是刚才讲了,传啥都行有个啥子意思勒。没啥子用。因此我们要限制所以就有了上下限的说法。

    class WildCard<T>{
        private T var;

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

        public T getVar(){
            return var;
        }

        public static void main(String[] args) {
            WildCard<?> wildCard = new WildCard<>();
            WildCard<Object> wildCard1 = new WildCard<>();
            // 有什么用呢?对比于Object 显然这样使用和Object没两样
            // 怎么才有用呢 配合 extends/super

            wildCard.toClass(new ArrayList());
            wildCard.toClass(new LinkedList());
        }

        // 此时即可限定传入的类型必须是实现List接口的类  当然你也可以在对象声明时进行限制
        public <T extends List > void toClass(T  temp){
            System.out.println(temp.getClass());
        }

    }

为什么没写一个super的案例呢?因为这样写不了

T extends Number 我不知道T是什么类型,但肯定是Number的子类,对于方法来说这可以父类类型接收子类对象。
T super Number 我不知道T是啥,但Number肯定是T的子类,对于方法来说,T是什么?大的转成小的需要强制类型转换的!!我不能自动给你处理强转

不理解看官方

Chapter 4. Types, Values, and Variablesicon-default.png?t=M0H8https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.5.1

那做什么用呢?看下面

上限通配符extend 取数据

extend 指定的类必须是继承某个类,或者实现了某个接口

? extend List 限定传入的类型必须是实现List接口的类 或者是List本身

List<? extends Animal> list = new ArrayList<Cat>();

只能取数据 保证取得的数据都是Animal可以接受的

下限通配符super 添数据

super 指定的类必须是自身 或者是其父类

? super List 限定传入的类型必须是List本身 或者是List的父类

List<? super Cat> list = new ArrayList<Animal>()

可以加入数据保证数据都是Cat 或者Cat的父类 Animal 

这是Java  Collections的copy方法源码 可以解释一下只能取和只能存的的意义各位读者自行感悟

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    int srcSize = src.size();
    if (srcSize > dest.size())
        throw new IndexOutOfBoundsException("Source does not fit in dest");

    if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
        for (int i=0; i<srcSize; i++)
            dest.set(i, src.get(i));
    } else {
        ListIterator<? super T> di=dest.listIterator();
        ListIterator<? extends T> si=src.listIterator();
        for (int i=0; i<srcSize; i++) {
            di.next();
            di.set(si.next());
        }
    }
}

泛型擦除

        开篇就讲过泛型是一种语法糖,因此泛型是不会跑到Java虚拟机中的。那么如何做到让虚拟机正常识别呢?那就是泛型擦除。

        有同学要问为什么不直接让虚拟机可以识别呢?泛型是JDK1.5才有的机制,为了向下兼容因此不能够直接在运行时就搞泛型。

        怎么擦除呢?看个例子就好了

你写的代码

public class Demo<T> {
    private T t;
    public static void main(String[] args) {
        
    }
    public T getT(){
        return t;
    }
    public void setT(T t){
        this.t = t;
    }
}

擦除后跑在虚拟机上的代码

public class Demo {
    private Object t;
    public static void main(String[] args) {
        
    }
    public Object getT(){
        return t;
    }
    public void setT(Object t){
        this.t = t;
    }
}

当然通配符也会被擦除掉

<? extends T> 擦到T

<?  super  T>         擦到Object

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值