java基础(泛型,枚举,反射)

2 泛型

泛型是JDK1.5及以上才可以使用的特性/语法,它的本质是 类型参数化(Parameterized by types).

2.1 概述

在声明一个类、接口、方法的时候,需要涉及到到一个问题:要给属性确定一个类型,或者给方法的返回值确定一个类型,或者给方法的参数确定一个类型。

之前,定义类、接口、方法的时候,上面所描述的类型都是直接写死,不会变化的。

public class Point{
   
    int x;
    int y;
}

Point类表示一个坐标点,它的的俩个属性x坐标和y坐标的类型是int,这个int是在定义Point类的时候就已经写好了,并且不会自动改变。

现在,希望Point类中的x属性和y属性的类型变的灵活一点,可以在将来使用的时候,临时通过传参的方式,来确定属性x和y的具体类型。

修改Point类的代码,添加泛型参数T:

public class Point<T>{
   
    T x;
    T y;
}

注意,T是泛型参数,表示一种数据类型,具体是什么类型,需要将来使用Point的时候进行传参来确定

注意,如果将来Point在使用的时候,没有给泛型参数T传值,那么T默认就表示为Object类型

注意,T是泛型参数的名字,也就是相当于形参,名字随便起,但是一般用一个有意义的大写字母

Point<T>类的使用:

public static void main(String[] args) {
   
    //p1对象中x和y的属性类型都是Integer
    Point<Integer> p1 = new Point<Integer>();
    p1.x = 1;
    p1.y = 2;
    
    //p2对象中x和y的属性类型都是String
    Point<String> p2 = new Point<String>();
    p2.x = "1";
    p2.y = "2";
    
    //p3对象中x和y的属性类型都是Object
    Point p3 = new Point();
    p3.x = new Object();
    p3.y = new Object();
    
}

可以看出,Point类的中x和y属性的类型,是可以根据我们在使用时所传的参数,进行临时变化的。

如果没有传这个泛型参数,那么这个参数T就默认是Object类型

注意,给泛型参照传的值,只能是引用类型,不能是基本类型:Point<int>编译报错

在类上面添加泛型参数后,在类中的任何可以使用类型的地方,都可以先使用这个泛型参数:

class Point<T>{
   
    private T x;
    private T y;

    public Point(){
   }

    public Point(T x, T y) {
   
        this.x = x;
        this.y = y;
    }

    public T getX() {
   
        return x;
    }

    public void setX(T x) {
   
        this.x = x;
    }

    public T getY() {
   
        return y;
    }

    public void setY(T y) {
   
        this.y = y;
    }
}

思考,为什么泛型也叫做 类型参数化(Parameterized by types)

2.2 集合的泛型

了解泛型的意思之后,接下来可以再看下之前学习过的集合中的泛型:

public interface Collection<E>{
   
    
    boolean add(E e);
    
}

Collection是一个泛型接口,泛型参数是E,在接口中,add方法的参数类型也使用了E。

那么就说明在使用Collection接口的时候,如果没有给泛型参数传值,那么这个E就默认表示为Object,add方法就可以接收任意类型的对象。

public static void main(String[] args) {
   
    //没有给泛型参数传值,那么泛型默认表示为Object类型
    Collection c = new ArrayList();
    c.add("hello1");
    c.add("hello2");
    c.add("hello3");
    c.add(1);

    for(Object obj:c){
   
        String str = (String) obj;
        System.out.println(str);
    }
    
}

//运行结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    
    

在这种情况下,集合中如果存储的数据类型不一致,就会在强制转换的时候出现类型转换异常

如果在使用Collection接口的时候,给泛型参数指定了具体类型,那么就会防止出现类型转换异常的情况,因为这时候集合中添加的数据已经有了一个规定的类型,其他类型是添加不进来的。

public static void main(String[] args) {
   
    Collection<String> c = new ArrayList<String>();
    c.add("hello1");
    c.add("hello2");
    c.add("hello3");
    //编译报错,add(E e) 已经变为 add(String e)
    //int类型的数据1,是添加不到集合中去的
    //c.add(1);

    for(String str : c){
   
        System.out.println(str);
    }
}

可以看出,传入泛型参数后,add方法只能接收String类型的参数,其他类型的数据无法添加到集合中,同时在遍历集合的时候,也不需要我们做类型转换了,直接使用String类型变量接收就可以了,JVM会自动转换的

Collection<String> c = new ArrayList<String>();

可以简写成:

Collection<String> c = new ArrayList<>();

Map接口使用泛型:

public interface Map<K,V>{
   
    V put(K key, V value);
    Set<Map.Entry<K, V>> entrySet();
}
public static void main(String[] args) {
   
    Map<Integer,String> map = new HashMap<>();
    
    //根据泛型类型的指定,put方法中的key只能是Integer类型,value只能是String类型
    map.put(1,"hello1");
    map.put(2,"hello2");
    map.put(3,"hello3");
    map.put(4,"hello4");
	
    //根据上面列出的源码可知,当前指定Map的泛型类型为:Map<Integer,String> map
    //entrySet方法返回的类型就应该是Set<Map.Entry<Integer, String>>
    Set<Map.Entry<Integer, String>> entrySet = map.entrySet();

    for(Map.Entry entry:entrySet){
   
        System.out.println(entry.getKey()+" : "+entry.getValue());
    }

}

2.3 泛型的种类

java中的泛型分三种使用情况:

  • 泛型类
  • 泛型接口
  • 泛型方法

泛型类,如果泛型参数定义在类上面,那么这个类就是一个泛型类

在类中,就可以使用这个T来代表某一个类型,这个类型具体是什么将来使用的时候再传参确定

public class Point<T>{
   ...}

public static void main(String[] args){
   
    Point<String> p = new Point<>();
}

泛型接口,如果泛型参数定义在接口上面,那么这个接口就是一个泛型接口

在接口中,就可以使用这个T来代表某一个类型,这个类型具体是什么将来使用的时候再传参确定

public interface Action<T>{
   ...}

public static void main(String[] args){
   
    //创建匿名内部类
    Action<String> a = new Action<>(){
   
        //...
    };
}

泛型方法,如果泛型参数定义在方法上面,那么这个方法就是一个泛型方法

public class Test{
   
    public <T> T test(T t){
   
        //..
    }
}

public static void main(String[] args){
   
    Test t = new Test();
    String str = t.test("hello");
    Integer i = t.test(1);
    Double d = t.test(10.5D);
}

可以看出,调用test方法时候,我们传什么类型的参数,test方法的返回类型就是什么类型的。

这种效果,在不使用泛型的情况下,是不可能实现的。

2.4 泛型的类型

先看俩种错误的情况:

//编译通过
//父类型的引用,指向子类对象
Object o = new Integer(1);

//编译通过
//Object[]类型兼容所有的【引用】类型数组
//arr可以指向任意 引用类型 数组对象
Object[] arr = new Integer[1];

//编译失败
//注意,这个编译报错,类型不兼容
//int[] 是基本类型数组
Object[] arr = new int[1];

//编译失败
//错误信息:ArrayList<Integer>无法转为ArrayList<Object>
//在编译期间,ArrayList<Integer>和ArrayList<Object>是俩个不同的类型,并且没有子父类型的关系
ArrayList<Object> list = new ArrayList<Integer>();

注意,=号俩边的所指定的泛型类型,必须是要一样的
注意,这里说的泛型类型,指的是<>中所指定的类型

虽然IntegerObject的子类型,但是ArrayList<Integer>ArrayList<Object>之间没有子父类型的关系,它们就是俩个不同的类型

所以,

Object o = new Integer(1);编译通过

ArrayList<Object> list = new ArrayList<Integer>();编译报错

也就是说,俩个类型,如果是当做泛型的指定类型的时候,就没有多态的特点了

2.5 通配符

观察下面代码:

public void test1(Collection<Integer> c){
   

}

public void test2(Collection<String> c){
   

}

public void test3(Collection<Object> c){
   

}

test1方法【只能】接收泛型是Integer类型的集合对象
test2方法【只能】接收泛型是String类型的集合对象
test3方法【只能】接收泛型是Object类型的集合对象

原因:由于泛型的类型之间没有多态,所以=号俩边的泛型类型必须一致

在这种情况下,就可以使用通配符(?)来表示泛型的父类型:

public void test(Collection<?> c){
   

}

注意,这时候test方法中的参数类型,使用了泛型,并且使用问号来表示这个泛型的类型,这个问号就是通配符,可以匹配所有的泛型类型

test方法可以接收 泛型是任意类型的 Collection集合对象

public static void main(String
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值