java 泛型

1、何为泛型?

泛型的本质便是类型参数化,通俗的说就是用一个变量来表示类型,这个类型只能是引用类型,表明可接受的类型。

2、为什么使用泛型?

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

1、 类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。

2、 消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
3、 潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。
使用泛型可以把使用Object的错误从运行后提前至编译后,及早地发现错误做出修改,而不是运行后再去找bug出现在哪。

3、使用泛型

下面是一个使用泛型地类地例子:

public class Generic<T> {

    private T value;

    public void test(T t){
        
    }

}

命名类型参数:

推荐的命名约定是使用大写的单个字母名称作为类型参数。这与 C++ 约定有所不同,并反映了大多数泛型类将具有少量类型参数的假定。对于常见的泛型模式,推荐的名称是:

E - Element (在集合中使用,因为集合中存放的是元素)

T - Type(Java 类)

K - Key(键)

V - Value(值)

N - Number(数值类型)

? - 表示不确定的java类型

这些是用的较多且大都认识的名称
实际上这么取名也是合法的:

public class Generic<Hello> {

    private Hello value;

    public void test(Hello hello){

    }

}

使用了泛型,使程序在编译时发出警告

public class Generic<Hello> {//定义的标识符号Hello

    private Hello value;//value的类型由外部决定

    public void test(Hello hello){//普通方法

    }

    public static void main(String[] args) {
        Generic<Integer> g = new Generic<>();//声明了类型为Integer的Generic类
        g.test("hello");//方法中使用了非Hello的类型
    }

}

使用了非Hello的类型编译器发出警告

test(java.lang.Integer) in Generic cannot be applied to java.lang.String)

4、类型擦除与原始类型

类型擦除:
在JAVA的虚拟机中并不存在泛型,泛型只是为了完善java体系,增加程序员编程的便捷性以及安全性而创建的一种机制,在JAVA虚拟机中对应泛型的都是确定的类型,在编写泛型代码后,java虚拟中会把这些泛型参数类型都擦除,用相应的确定类型来代替,代替的这一动作叫做类型擦除,而用于替代的类型称为原始类型,在类型擦除过程中,一般使用第一个限定的类型来替换,若无限定则使用Object。
看看如下代码:

List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
		
System.out.println(l1.getClass() == l2.getClass());

输出为true,因为List<String>和List<Integer>在jvm中的类型都是List。

泛型信息被擦除了,是通过泛型转译实现的。

1、 泛型代码(不带限定):

public class Generic<T> {

    private T value;

    public void test(T t){
        
    }

}

虚拟机翻译后的原始类型:

public class Generic
{
    private Object value;
    public void test(Object t)
    {
        
    }
}

2、泛型类(带限定):

public class Generic<T extends Comparable> {

    private T value;

    public void test(T t){

    }
}

虚拟机翻译后的原生类型:

public class Generic<T extends Comparable> {

    private Compareble value;

    public void test(Compareble t){

    }

类型擦除时类型的参数部分没有指定上限则会转译为Object类型,如果指定了上限如 <T extends Compareble>则类型参数就被替换成类型上限。这样在反射中调用方法时应该考虑类型擦除后的参数类型。

5、通配符 ‘?’

  • <?>被称作无限定的通配符。
  • <? extends T>被称作有上限的通配符。
  • <? super T>被称作有下限的通配符。

无限定通配符 <?>

无限定通配符提供了只读的功能,经常与容器类配合使用,它其中的 ? 其实代表的是未知类型,所以涉及到 ? 时的操作,一定与具体类型无关。

public void testWildCards(Collection<?> collection){
}
List<?> wildlist = new ArrayList<String>();
wildlist.add(123);// 编译不通过

其作用可能是:提高了代码的可读性,其他人看到这段代码时,就能够迅速对此建立极简洁的印象,能够快速推断源码作者的意图。

有上限通配符<? extends T>

<?>代表着类型未知,但是我们的确需要对于类型的描述再精确一点,我们希望在一个范围内确定类别,比如类型 A 及 类型 A 的子类都可以。
<? extends T> 代表类型 T 及 T 的子类。

 public void testSub(Collection<? extends Base> para){ }

上述代码丧失了写操作的能力,即:

para.add(new Sub()); para.add(new Base()); 

仍然编译不通过

<? super T> 和 <? extends T>相对应

<? super T>代表T及其子类
<? super T>是具有一定写的能力的,

 public void testSuper(Collection<? super Sub> para){ 
 para.add(new Sub());//编译通过 
 para.add(new Base());//编译不通过 
 }

用泛型方法取代通配符是可以通过强转进行操作的。

public <T> void test(Collection<T> collection){
	collection.add((T)new Integer(12));
	collection.add((T)"123");
}

参考:https://blog.csdn.net/briblue/article/details/76736356

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值