Java泛型的简单总结

最近在imooc上面买了一个有关spring源码分析的课程,前几天花了不少时间才把spring源码编译成功,真的太花时间了,这几天就开始了spring的学习,今天在学习课程的时候,老师讲解到了泛型。泛型我在以前确实学过,但是随着时间的推移,逐渐忘记了,今天就想来总结一个泛型的使用。学无止境,好好学习吧,少年,加油!

个人博客:大碗稀饭

泛型的好处

(以下内容来自百度百科)

Java语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了很大的改动,许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:

类型安全

泛型的主要目标是提高Java程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在非常高的层次上验证类型假设。没有泛型,这些假设就只存在于系统开发人员的头脑中。

通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误就可以在编译时被捕获了,而不是在运行时当作ClassCastException展示出来。将类型检查从运行时挪到编译时有助于Java开发人员更早、更容易地找到错误,并可提高程序的可靠性。

消除强制类型转换

泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。尽管减少强制类型转换可以提高使用泛型类的代码的累赞程度,但是声明泛型变量时却会带来相应的累赞程度。在简单的程序中使用一次泛型变量不会降低代码累赞程度。但是对于多次使用泛型变量的大型程序来说,则可以累积起来降低累赞程度。所以泛型消除了强制类型转换之后,会使得代码加清晰和筒洁。

更高的运行效率

在非泛型编程中,将筒单类型作为Object传递时会引起Boxing(装箱)和Unboxing(拆箱)操作,这两个过程都是具有很大开销的。引入泛型后,就不必进行Boxing和Unboxing操作了,所以运行效率相对较高,特别在对集合操作非常频繁的系统中,这个特点带来的性能提升更加明显。

潜在的性能收益

泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,Java系统开发人员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的JVM的优化带来可能。

泛型的实现原理

泛型的实现是靠类型擦除技术 类型擦除是在编译期完成的 也就是在编译期 编译器会将泛型的类型参数都擦除成它的限定类型,如果没有则擦除为object类型之后在获取的时候再强制类型转换为对应的类型。 在运行期间并没有泛型的任何信息,因此也没有优化。

验证泛型的实现原理

为了验证泛型在编译的时候使用了类型擦除技术,在运行期间不会包含任何泛型的信息,我们使用IDEA打开泛型编译后的class文件,就可以进行查看。

//定义泛型类
public class GenericFactory<T> {
    T member;
    GenericFactory(T member){
        this.member = member;
    }

    public T getMember() {
        return member;
    }
}

//使用泛型类
public class GenericDemo {
    public static void main(String[] args) {
        GenericFactory<Integer> integerGenericFactory = new GenericFactory<Integer>(123);
        GenericFactory<String > stringGenericFactory = new GenericFactory<String>("cxf");

        System.out.println(integerGenericFactory.getMember());
        System.out.println(stringGenericFactory.getMember());
    }
}

//使用IDEA中控制台,然后使用 javac *.java,编译完成后,我们在打开GenericDemo查看
//我们可以清楚的看到在,编译完成后,泛型已经被擦除了,所以在运行的时候不会有泛型的任何信息

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.generic;

public class GenericDemo {
    public GenericDemo() {
    }

    public static void main(String[] var0) {
        GenericFactory var1 = new GenericFactory(123);
        GenericFactory var2 = new GenericFactory("cxf");
        System.out.println(var1.getMember());
        System.out.println((String)var2.getMember());
    }
}

泛型的使用

泛型在Java中主要用于泛型接口,泛型类,泛型方法,在下面呢,我也会一一举例。

我们需要注意:泛型不支持基本数据类型

代码实现

/**
 * 泛型接口
 */
public interface GenericFactory<T,N> {
    T nextObject();
    N nextNumber();
}


import java.util.Random;
/**
 * 泛型类:实现一
 */
public class RobotFactory implements GenericFactory<String,Integer> {

    private Integer[] integers = {1,2,3,4,5,6};
    private String[] strings = {"qwe","asd","zxc","rty","fgh","vbn"};
    @Override
    public String nextObject() {
        Random random = new Random();
        return strings[random.nextInt(6)];
    }

    @Override
    public Integer nextNumber() {
        Random random = new Random();
        return integers[random.nextInt(6)];
    }

    public static void main(String[] args) {
        GenericFactory genericFactory = new RobotFactory();

        System.out.println(genericFactory.nextNumber());
        System.out.println(genericFactory.nextObject());
    }
}

/**
 * 泛型类:实现二
 * 注意:①GenericFactoryDemo<T,N>中T和N的顺序是可以交换的。
 *      ②GenericFactoryDemo<T,N>与GenericFactory<T,N>泛型的字母必须相同
 */
public class GenericFactoryDemo<T,N> implements GenericFactory<T,N>{
    @Override
    public T nextObject() {
        return null;
    }

    @Override
    public N nextNumber() {
        return null;
    }
}

/**
 * 泛型方法的使用
 */
public class GenericMethod {

    /**
     * 泛型方法
     */
    public static <E> void printElements(E[] elements){
        for(E e : elements){
            System.out.printf("%s",e);
            System.out.print(" ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Integer[] integers = {1,2,3,4};
        Double[] doubles = {1.1,1.2,1.3,1.4};
        String[] strings = {"qwe","asd","zxc","cxf"};

        printElements(integers);
        printElements(doubles);
        printElements(strings);
    }
}

泛型统配符的使用

泛型统配符指的是 ? 这个符号,但是使用统配符虽然好用,但是也存在巨大的坑

public class GenericFactory<T> {
    T member;
    GenericFactory(T member){
        this.member = member;
    }
    public T getMember() {
        return member;
    }
}


public class GenericDemo {

    /**
     * 加法运算
     */
    public static void addition(GenericFactory<? super Integer> genericFactory){
        int result = 111 + (Integer) genericFactory.getMember();
        System.out.println(result);
    }


    public static void main(String[] args) {
        GenericFactory<Integer> integerGenericFactory = new GenericFactory<>(123);
        GenericFactory<String> stringGenericFactory = new GenericFactory<>("cxf");

//      条件:public static void addition(GenericFactory<Integer> genericFactory)
//      addition(integerGenericFactory);//输出结果为234

//      条件:public static void addition(GenericFactory<?> genericFactory)
//       这个时候下面方法在编译的时候不会报错,但是在运行的时候会抛出ClassCastException
//        所以说 统配符? 其实就相当于潘多拉宝盒,用起来好用,但是一不小心就会抛出异常
//        如何解决这个问题,我们可以给 通配符? 添加相关界限
//        addition(stringGenericFactory);

//      条件:public static void addition(GenericFactory<? extends Number> genericFactory)
//        给通配符添加上界限 使用规则是 ? extends xxx
//        意思就是,我们传递过来的类必须是xxx的子类,所以也叫做通配符的上界限
//        addition(integerGenericFactory);
//        addition(stringGenericFactory);//这个时候就会在编译的时候报错

//      条件:public static void addition(GenericFactory<? super Integer> genericFactory)
//        我们还可以给统配添加下界限 使用规则是 ? super xxx
//        意思就是,我们传递过来的类必须是xxx的父类,所以也叫做通配符的下界限
//          addition(integerGenericFactory);
//        addition(stringGenericFactory);//这个时候就会在编译的时候报错
    }
}

泛型字母的含义

  • E-Element : 在集合中使用,因为集合中存放的是元素
  • T-Type : Java类
  • K-Key : 键
  • V-Value : 值
  • N-Number : 数值类型
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值