Java泛型学习

由于有时候调用父类向下转型为子类时,若其对象本身创建时属于父类,则会抛出一个运行时的异常。

public class Person {
    private String name;
   // 省略构造方法和getter和setter	
}

public class Student extends Person {
    private String classroom;
	// 省略构造方法和getter和setter	
}

public class Main {
    public static void main(String[] args){
   	//第一部分
        Person aPerson = new Student("小王", "高三一班");
        Student student = (Student) aPerson;

        System.out.println(student.getClassroom());
// 第二部分
        Person bPerson = new Person("小李");
        Student bStudent = (Student) bPerson;
        System.out.println(bPerson.getName());
    }
}

在上述运行中,第一部分由于本来就是子类的对象,向下转型没问题,但是第二部分向下转型就会有问题。

第一部分即使为子类对象,但是我们知道,子类对象在实例化时也会调用父类的构造方法,所以子类对象的堆内存其实会存有父类的属性。所以即使向下转型也没有问题
第二部分中父类实例化不会去调用子类的构造方法,强制类型转换的话,父类的堆内存的对没有子类属性,子类在父类的对象内找不到其属性。所以会出问题。(个人理解,如有误请斧正,谢谢)

下图来自魔乐科技
在这里插入图片描述

个人案例分析:

案例一
       Person [] people = new Person[5];
       Teacher teacher = new Teacher("李老师", 5000.0);
       Student student = new Student("小王", "高三一班");
       people[0] = teacher;
       people[1] = student;

       for (Person temp:
            people) {
           System.out.println(((Teacher)temp).getSalary());
       }

此时看出teacher转换为student时,肯定会出错。这就是使用父类类型接收后需要向下转型的问题。

加入泛型
  1. 为了慢慢解决这种对象的向下转型的安全隐患问题,JDK1.5后加入了泛型,使类中的属性、方法或者参数可以通过运行时来决定。

  2. 泛型定义完成后可以在对象实例化时进行泛型类型的设置。

  3. 注意:

    1. 泛型在使用时只允许设置引用类
  4. 对于只提供访问,不提供修改的方法,可以使用通配符?来接收所有类型。
    使用? extends 类来设置泛型的上限(只允许是指定的或其子类)
    使用? super 类来设置泛型的下限(只允许是指定的其父类)

参考ArrayList源码后,个人做了一个简易版的MyArray

public class MyArray<T> {
    private final Object[] array;
    public int index = 0;
    public int size;

    public MyArray(int size) {
        array = new Object[size];
        this.size = size;
    }

    public MyArray() {
        this.array = new Object[5];
        this.size = 5;
    }

    public boolean add(T t) {
        if (this.index >= this.size) {
            return false;
        } else {
            this.array[index] = t;
            index++;
            return true;
        }
    }

    public T get(int index) {
        return (T) this.array[index];
    }

    public Object[] getArray() {
        return Arrays.copyOf(array, index);
    }
}

重点来了,在于get方法,为何此时我们可以直接向下转型呢?因为添加对象调用的add方法添加的对象是确定的T类型,传入的对象原本一定是T类型,所以可以向下转型。
此时上述案例代码可以改为

	    MyArray<Teacher> myArray = new MyArray<>();
        Teacher teacher = new Teacher("李老师", 123.5);
       // youngTeacher 是teacher的一个子类
        Teacher youngTeacher = new YoungTeacher("xxx");
        Student student = new Student("小王", "高三一班");
        
       /* 下面这注释掉的一句,如果取消注释会发现编译时候就不能通过了,所以保证
      了数组存放内容的唯一确定。     
	*/
//        myArray.add(student);
        myArray.add(teacher);
        myArray.add(youngTeacher);
        
        Object[] result = myArray.getArray();
        for (Object temp:
             result) {
            Teacher tempTeacher = (Teacher) temp;
            System.out.println(tempTeacher.getSalary());
        }
案例二

个人参考第一个案例的感悟写出了第二个案例。可能与现实生活有很大区别,但是原理应该是没问题的。

card类是抽象类,InterCard是只能进行整存争取的卡片,Double是可以进行浮点存取的卡。

public abstract class Card<T extends Number> {
    private T balance;

    public Card(T balance) {
        this.balance = balance;
    }

    public boolean addCheck(T t) {
        if (this.check(t)) {
            return this.add(t);
        }
        return false;
    }

    protected abstract boolean add(T t);

    private boolean check(T t) {
   	// 此处小偷懒,直接使用了其double值
        return t.doubleValue() > 0;
    }

    public T getBalance() {
        return balance;
    }

    protected void setBalance(T balance) {
        this.balance = balance;
    }
}

public class DoubleCard extends Card<Double> {

    public DoubleCard(Double balance) {
        super(balance);
    }

    public DoubleCard() {
   	/*
   一定要调用父类有参构造,否则会有空指针异常,个人认为原因应该是balance未初始化,
  因为是泛型类型,所以父类无法提供默认赋值。
  */
        super(0.0);
    }

    @Override
    protected boolean add(Double aDouble) {
        this.setBalance(this.getBalance() + aDouble);
        return true;
    }
}

public class InterCard extends Card<Integer> {
    public InterCard(Integer balance) {
        super(balance);
    }

    public InterCard() {
   /*
   一定要调用父类有参构造,否则会有空指针异常,个人认为原因应该是balance未初始化,
  因为是泛型类型,所以父类无法提供默认赋值。
  */
        super(0);
    }

    @Override
    public boolean add(Integer integer) {
        this.setBalance(this.getBalance() + integer);
        return true;
    }
}

public class Main {
    public static void main(String[] args){
        Card<Integer> card = new InterCard();
        card.addCheck(100);
        Card<Double> doubleCard = new DoubleCard();
        doubleCard.addCheck(2000.52);

        System.out.println(card.getBalance() + "\n" + doubleCard.getBalance());
    }
}

总结

因为对象向下转型可能会抛出异常,所以个人认为泛型只是可以限定容器存放的内容的类型,其实也就是以父类为类型的对象中保存的子类的类型是其子类或子类的子类,转换为子类就不会出现运行时错误了。

个人码云示例连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值