文章目录
1. 什么是泛型
泛型,即“参数化类型”,将参数的类型泛化。在声明时不指定参数类型,根据传入的实际参数的类型来决定。是jdk1.5的特性
泛型可以随便写,但默认的规则为:
- E—Element,常用在Java的集合中
- K,V—–key,value 代表Map中的键值对
- N—–Number 代表数字
- T—-Type 类型,如String,Integer等等
注意:基本数据类型不能作为泛型
在集合中不使用泛型时,可以存储任意类型的
对象
,集合中的元素是对象。
泛型在编译期有效,可以
动态修改参数类型
,泛型的底层主要是通过list源码来实现的。
public static void main(String[] args) {
List<String> list=new ArrayList();
List<Double> list2=new ArrayList();
//通过反射获取class文件
Class aClass = list.getClass();
Class aClass1 = list.getClass();
System.out.println(aClass.equals(aClass1));
}
//输出结果为true,由此可得泛型只在编译期有效,相同的代码,不同的泛型编译以后的class文件相同
2. 泛型的使用方式
泛型的使用方式有三种分别为:泛型类、泛型接口、泛型方法。
泛型类
主要规范类型的类型,一个类可以有多个泛型。
public class tyuio<S,N,E>{
private S name;
private N age;
public tyuio(S name, N age) {
this.name = name;
this.age = age;
}
public S getName() {
return name;
}
public void setName(S name) {
this.name = name;
}
public N getAge() {
return age;
}
public void setAge(N age) {
this.age = age;
}
@Override
public String toString() {
return "tyuio{" +
"name=" + name +
", age=" + age +
'}';
}
public static void main(String[] args) {
tyuio t=new tyuio<String,Integer,String>("123",4);//在实例化对象时,可指定泛型,也可不指定泛型。
//若在实例化时要指定泛型的参数,则必须全部指定,不能部分指定
System.out.println(t);
}
}
//编译后的代码为
public static void main(String[] paramArrayOfString)
{
tyuio localtyuio = new tyuio("123", Integer.valueOf(4));//此处没有了泛型,且对象名与设定的不同,对4进行了装箱操作转为Integer类型的一个对象
System.out.println(localtyuio);
}
}
泛型接口
创建方式与泛型类相同。
public interface qwr<T> {
public void eat(T name);
}
class tyuio implements qwr{
public static void main(String[] args) {
}
@Override
public void eat(Object name) {
}
}
class tyuio implements qwr<String>{
public static void main(String[] args) {
}
@Override
public void eat(String name) {
}
}
//编译后的代码都为
class
{
}
class tyuio
implements qwr
{
public static void main(String[] paramArrayOfString)
{
}
public void eat(Object paramObject)
{
}
}
在使用泛型类和泛型接口时需注意
-
泛型类和接口主要在继承和实现时使用。
-
未传入泛型的实参时,与泛型类定义的相同,在声明类时,需要将泛型的声明也一起加到类中
class a<T>{ public void eat(Object name) { // return null; } } class b<T> extends a<T>{ public static void main(String[] args) { b w=new b(); } }
-
接口和类在被继承的时候指定具体的类型,子类将不需要泛型,但所有在接口和父类中使用泛型的地方都要替换成传入的实参类型
class a<T>{ public T eat(T name) { return null; } } class b extends a<String>{ //即b可以不使用泛型,在a中使用泛型的地方都变为了String @Override public String eat(String name) { return null; } public static void main(String[] args) { b w=new b(); } }
public interface qwr<T> { public T eat(T name); } public class tyuio implements qwr<Integer>{ @Override public Integer eat(Integer name) { return null; } public static void main(String[] args) { tyuio t=new tyuio(); } }
泛型方法
一个方法由于传入的参数不同,最终输出的结果也不同。
在泛型方法中 :代表声明此方法为泛型方法,类型为T, F :代表方法的返回值类型
表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
class a{ //方法的返回值类型
public <F> F eat(String name) {
//泛型方法
return null ;
}
}
基本用法
class a<T> {
//此类是一个泛型类
private T name;
/*此方法不是泛型方法,因为没有<T>,它是一个普通的成员方法,
因为泛型类已经声明了泛型T,所以此处的T可以使用,该方法的返回值类型为T类型
*/
public T getName(T d) {
return name;
}
/*d
下列方法是错误的,因为类的声明中并未声明泛型B,泛型B在作为返回值和形参时无法被编译器识别。
public B getName(B d) {
return name;
}*/
public void setName(T name) {
this.name = name;
}
/*下列方法为一个泛型方法,主要体现在使用了<T>,<T>表明该方法为一个泛型方法,并且声明了一个泛型T,T可以出现在任意位置,需注意的是此处的T与泛型类中声明的T是不同类型的*/
public <T> T lok(a<T> name) {
T d = null;
return name.getName(d);
}
/*此方法正确,E在public 后被声明(方法声明是被声明),所以即使泛型类中未声明也可以在该方法中可以使用,编译器也能正确识别泛型方法中的泛型
*/
public <E> E lok(E name) {
E d = null;
return d;
}
/*
此方法错误因为此方法只是声明了泛型E并未声明N
public <E> E lok(N name) {
E d = null;
return d;
}*/
/*
此方法错误,对于编译器来说N并没有在项目中被声明过,因此编译器不知道如何编译N
所以此方法不是一个正确的泛型方法的声明
public void lok(N name) {
}
*/
}
泛型方法与可变参数
public void d(T...arg){
for (T t:arg) {
System.out.println(t);
}
}
静态方法与泛型
需注意:静态方法无法访问类上定义的泛型。如果静态方法操作的引用数据不确定的时候,必须要将泛型定义在方法上。即
如果静态方法要是用泛型的话,必须将静态方法定义为泛型方法。
class o<T>{
public static void d(T...arg){
System.out.println(arg);
}
//此方法会报错,在静态方法中使用泛型时,无论该泛型类型是否在泛型类中声明,必须将静态方法设定为泛型方法。
public static <T> void d2(T...arg){
System.out.println(arg);
}
//此静态方法使用的泛型正确
public static void main(String[] args) {
d();
d2();
}
}
泛型的通配符和上下限
通配符一般是用
?
代替具体的类型参数。
说明
- 在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义
-
?
表示不限定通配符,通常与集合配合使用 - 集合中所用类型,不继承父子关系。如:Base是基类,Child为子类,则List和List不具备继承关系,并且而这没有任何关系
-
<? extends T>
称为上限通配符,表示T类型及其T子类 -
<? super T>
称为下限通配符,表示T类型及其父类
创建对象时的通配符(new 集合时)
有时候在创建集合时,并不知道集合中要存放的数据是什么,所以就会使用到通配符,案例见:–上限通配符
<? extends T>
–和–下限通配符
<? super T>
–
无限定通配符<?>
- 通常与集合配合使用
-
集合中的限定类型,表示集合中只能包含一种类型;不限定类型,表示不限定任何类型。
一旦集合转为不限定类型,不可以向集合中添加数据,只能进行查询集合长度、判断集合是否为空等一些与集合中数据的类型无关的一些操作。
-
通常与
<? extends T>
和
<? super T>
配合使用
class o{
public static void main(String[] args) {
List l=new ArrayList();
l.add("123");
l.add("as");
l.add("aca");
List <?> l1=l;
l1.add("123");//此处会编译报错,因为一旦集合转为不限定类型,不可以向集合中添加数据
System.out.println(l1.size());
System.out.println(l1.isEmpty());
List<String> l2=new ArrayList();
l2.add("2345");
l2.add("345");
System.out.println(l2.size());
System.out.println(l2.isEmpty());
List<Double> l3=new ArrayList();
l3.add(2.3);
System.out.println(l3.size());
System.out.println(l3.isEmpty());
}
}
上限通配符
<? extends T>
<? extends T>
<? extends T>
:表示的意思为T类型及其子类
-
表示规定范围的类型。比如
<? extends T>
:规定的类型是T类型及其子类 - 表示的集合不能添加元素。
class o{
public static void main(String[] args) {
List l=new ArrayList();
l.add("123");
l.add("as");
l.add("aca");
List <? extends String> l1=l;
// l1.add("234"); 不可以添加元素
l1.remove(2);
Iterator iterator=l.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println(l1.size());
System.out.println(l1.isEmpty());
}
}
下限通配符
<? super T>
<? super T>
<? super T>
:表示T类型及其父类
- 表示规定范围的类型。
- 在集合中可以添加元素,也可以查询
class o{
public static void main(String[] args) {
List l=new ArrayList();
l.add("123");
l.add("as");
l.add("aca");
List <? super String> l1=l;
l1.add("234");
// l1.remove(2);
Iterator iterator=l.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println(l1.size());
System.out.println(l1.isEmpty());
for (int i = 0; i < l1.size(); i++) {
System.out.println(l1.get(i));
}
}
}
3.总结
泛型的提出只要是为了规范代码中的数据类型,防止在某些地方随便使用。有了泛型就不需要做类型转换了。