Java-泛型-1
Posted 唐微港
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java-泛型-1相关的知识,希望对你有一定的参考价值。
什么是泛型
泛型是Java SE 1.5 的特性,本质是参数化类型。用于区别于原生类型,作为限定传参,方法的类型等。并且类型明确的这种工作放到创建对象或者在调用方法的时候才去明确的具体类型的特殊类型。例如List list = new ArrayList(),这种list可以存放任意类型,而List list = new ArrayList(), 这种在使用的时候将T替换为了其他类型,那么就只能在list容器中存放为替换的其他类型,这样能够较好的防止运行时报错,保证了安全性。
当我们在集合的时候不管是指定了泛型还是未指定,在给容器中放入各种各样类型的值,比如int,boolean,string.其实这些类型都发生了默认的转型,都转化为了Object类型,例如int转化为了Integer, boolean转化为了Boolean。
List list = new ArrayList();
list.add(1);
list.add(true);
list.add("good");
不使用泛型的问题
如果不使用泛型,那么在使用到容器中的值的时候可能就会报类型转化异常的错误
public static void main(String[] args) {
List list = new ArrayList();
list.add("String 类型");
list.add(1);
list.add(true);
//next()方法是按照容器的存储顺序,逐一取出第一个到最后一个对象。
Iterator i = list.iterator();
while (i.hasNext()) {
String s = (String) i.next();
System.out.println(s.length());
}
}
错误信息:
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
报错的原因是:
while循环中,先用i.next()方法取出第一个是string类型的元素,然后打印输出的时候会换为String类型(因为存进去的时候默认转为是Object类型,使用的使用要向下转型),接着循环,迭代器取出第二个元素:1,但Integer类型不能强制装换为String类型,所以抛出异常:ClassCastException:类型转换异常。
所以这里就可以看出泛型的不是泛型的问题了,如果使用了泛型,那么在使用的时候就要明确能存放什么类型的数据,那么在存放数据到容器的时候,就全部都是同一类型的数据。
使用泛型的好处
除了解决上面的抛出异常问题,还可以使用泛型的方式对类,接口,方法等进行提取封装,避免过多的代码冗余。实现一次定义,多处使用。泛型的好处是在编译的时候检查类型安全
注意
需要注意的是泛型的类型参数必须是引用类型
(类、接口、数组等都是 引用类型
)而不能是简单类型,如 Generic<int> generic = new Generic<>(123);
是不允许的
泛型通配符
通配符:T、E、K、V、N、?
- E - Element (在集合中使用,因为集合中存放的是元素),E是对各方法中的泛型类型进行限制,以保证同一个对象调用不同的方法时,操作的类型必定是相同的。
- T - Type(Java 类),T代表在调用时的指定类型。
- K - Key(键) 在map的类型限定中用的较多
- V - Value(值) 在map的类型限定中用的较多
- N - Number(数值类型)
- ?- 表示不确定的java类型,是类型通配符,代表所有类型。?不会进行类型推断
其实,T、E、K、V、N这几种通配符看似各不相同,实际上并没有太大区别,只不过是一个约定好的字母标识。其实也可以使用大写字母A,B,C…X,Y,Z 等定义,就都是泛型了,例如把T换成A也一样表示,这里T只是名字上的意义而已
泛型的定义格式
下面只是简单的举个例子,其实还有类似于List<? extends T> , T 这些方式,具体的情况需要在实际使用中定义。
泛型接口
public class StdMain implements ParadigmInterface{
@Override
public Object getParadigmMethod(Object o) {
return null;
}
}
interface ParadigmInterface<T>{
T getParadigmMethod(T t);
}
泛型类
public class StdMain extends ParadigmInterface {
@Override
Object getParadigmMethod(Object o) {
return getParadigmMethod(o);
}
}
class ParadigmInterface<T> {
<T> T getParadigmMethod(T t) {
return t;
}
}
泛型方法
//泛型方法
class ParadigmInterface<T> {
<T> T getParadigmMethod(T t) {
return t;
}
}
泛型的擦除
泛型是运用在编译时期的技术:编译时编译器会按照<类型名>的类型对容器中的元素进行检查,检查不匹配,就编译失败。如果全部检查成功,就编译通过,编译通过后产生的.class文件中并没有<类型名>这个标识,也就是编译之后就不存在泛型了,而是将泛型定义替换为了指定类型,这就是泛型的擦除。
//1.定义泛型
list<T> list = new ArrayList<>()
//2.指定类型
list<String> list = new ArrayList<>()
//3.编译后就是上面指定类型的那种了
在.java文件运用泛型技术时,编译器在文件编译通过后就会自动擦除泛型标识。由于泛型的擦除,所以类文件中就不存在泛型机制。
泛型的补偿
编译器在擦除泛型后,会自动将类型转换为原定义的"泛型"(即指定的类型),这样就不必再做向下类型转换了,泛型的擦除和补偿 这两个机制都是编译器内部自动完成的,不需要手动实现。所以泛型的擦除和补偿其实就是一个互相替换的过程
泛型的限定 ?
我们知道使用泛型类时:如果明确参数类型,那么泛型就代表一种类型;如果使用通配符?,那么泛型就代表任意类型。但有时候我们希望指定某些类型(不是一个,也不要所有)能作为参数类型,这应该怎么办呢?
Java中利用泛型的限定解决了这个问题,即泛型的限定。我们只需要按这样的格式书写:
上限:<? extends E>表示参数类型是E及其所有子类
下限:<? super E>表示参数类型是E及其所有超类(即父类)
例:
//变量赋值或变量声明时候使用
List<?> list;
List<? extends Number> nList;
List<? super Integer> iList;
//可以接受任何继承自T的类型的List
List<? extends T>
//可以接受任何T的父类构成的List
List<? super T>
//可以接受List<Integer>或List<Float>
List<? extends Number>
以上是关于Java-泛型-1的主要内容,如果未能解决你的问题,请参考以下文章
什么意思 在HashMap之前 ? Java中的泛型[重复]