JAVA5特性---泛型

泛型

问题引出

1、要求:定义一个表示坐标的类(Point),该类要保存以下几种坐标:

· 整数:x = 10、y = 20;
· 小数:x = 10.2、y = 20.3;
· 字符串:x = 东经20度、y = 北纬15度。

Point类的设计关键在于x和y的类型,必须有一种类型可以保存这三类数据,首先想到的是Object类型:

· int:int自动装箱为Integer,Integer向上转型为Object;
· double:double自动装箱为Double,Double向上转型为Object;
· String:直接向上转型为Object;

范例:设计如下

class Point{ // 定义坐标类
    private Object x;
    private Object y;

    public Object getX() {
        return x;
    }

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

    public Object getY() {
        return y;
    }

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

范例:测试上述代码

public class Demo {
    public static void main(String[] args) {
        // 1. 设置数据
        Point pA = new Point();
        pA.setX(10);
        pA.setY(20);
        // 2. 取出数据
        int x = (Integer) pA.getX();
        int y = (Integer) pA.getY();
        System.out.println("(" + x + " , " + y + ")");
    }
}

上述代码利用Object数据类型解决了问题,但依然可能有一定问题。
范例:造成异常的代码:

public class Demo {
    public static void main(String[] args) {
        // 1. 设置数据
        Point pA = new Point();
        pA.setX("东经10度");
        pA.setY(10);
        // 2. 取出数据
        String x = (String) pA.getX();
        String y = (String) pA.getY();
        System.out.println("(" + x + " , " + y + ")");
    }
}

上述异常产生的原因是因为设置时存放的是int(Integer),而取出时是String。两个没有任何关系的类对象之间发生强制转换,会出现java.lang.ClassCastException错误。
2、向上转型是为了统一参数,向下转型是为了调用子类定义的特殊功能。向下转型是一种不安全的操作,那么这种操作应该在代码运行前排查出来。
从JDK1.5增加了泛型,泛型的核心作用在于:类在定义的时候,可以使用一个标记,此标记动态表示类中属性或方法参数的类型,使用时设置具体类型。

// T在Point类定义上只表示一个标记,使用时需要为其设置具体的类型
class Point<T> { // 定义坐标,Type = T
    private T x; // 该属性类型未知,由Point动态设置
    private T y; // 该属性类型未知,由Point动态设置

    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;
    }
}

在使用Point类时,才设置标记的类型,即设置类中属性的类型。
范例:设置为String

public class Demo {
    public static void main(String[] args) {
        // 1. 设置数据
        Point<String> pA = new Point();
        pA.setX("东经10度");
        pA.setY("北纬20度");
        // 此时Point类的类型为String,不需要向下转型
        // 2. 取出数据
        String x = pA.getX();
        String y = pA.getY();
        System.out.println("(" + x + " , " + y + ")");
    }
}

3、使用泛型后,类中属性的类型都是动态设置的,这样避免了向下转型的问题。但是泛型只能用于类,即不能用于基本数据类型,只能是引用类型(例如,不能用<int>,只能用<Integer>

public class Demo {
    public static void main(String[] args) {
        // 1. 设置数据
        Point<Integer> pA = new Point();
        pA.setX(10);
        pA.setY(20);
        // 利用包装类的自动装箱和自动拆箱
        // 2. 取出数据
        int x = pA.getX();
        int y = pA.getY();
        System.out.println("(" + x + " , " + y + ")");
    }
}

4、对于泛型有两点说明:

· 使用泛型类或接口时,没有设置接口的具体类型会出现编译警告,为了保证程序不出错,将默认使用Object表示。

public class Demo {
    public static void main(String[] args) {
        // 1. 设置数据
        Point pA = new Point(); // 将使用Object描述泛型
        pA.setX(10);
        pA.setY(20);
        // 2. 取出数据,需要转型
        int x = (Integer) pA.getX();
        int y = (Integer) pA.getY();
        System.out.println("(" + x + " , " + y + ")");
    }
}

· JDK1.7开始,可以简化泛型声明

Point<Integer> pA = new Point();

即实例化时只要在前面声明一个泛型的具体类型。

通配符

范例:观察下述程序

class Message<T> {
    private T msg;

    public void setMsg(T msg) {
        this.msg = msg;
    }

    public T getMsg() {
        return msg;
    }

}

public class Demo {
    public static void main(String[] args) {
        Message<String> m = new Message<String>();
        m.setMsg("Hello");
        fun(m); // 引用传递
    }

    public static void fun(Message<String> temp) {
        System.out.println(temp.getMsg());
    }
}

上述代码为Message类设置String型的泛型,但是此时设置其他类型时,fun()中的Message<String>就不能使用了,并且fun()不能针对不同的泛型进行重载,因为方法重载只认得参数类型,无法辨别泛型的不同。
解决方法一:不设置方法参数的泛型

public class Demo {
    public static void main(String[] args) {
        Message<Integer> mA = new Message<Integer>();
        Message<String> mB = new Message<String>();
        mA.setMsg(100);
        mB.setMsg("Hello");
        fun(mA); // 引用传递
        fun(mB);
    }

    public static void fun(Message temp) {
        System.out.println(temp.getMsg());
    }
}

此时fun()存在警告,因为不设置具体泛型,就会存在警告。并且存在下述问题:

public class Demo {
    public static void main(String[] args) {
        Message<Integer> mA = new Message<Integer>();
        mA.setMsg(100);
        fun(mA); // 引用传递
    }

    public static void fun(Message temp) { // 不设置泛型,默认为Object型
        temp.setMsg("Hello"); // 设置Striing型
        System.out.println(temp.getMsg());
    }
}

1、上述代码说明,需要一种方式可以接收任意的泛型类型,但是不可以修改,只能取出。就可以使用?来描述

public class Demo {
    public static void main(String[] args) throws Exception {
        Message<Integer> num = new Message<>();
        num.setMsg(100);
        fun(num);
    }

    public static void fun (Message<?> tmp) {
        //tmp.setMsg("String"); 报错,无法应用
        System.out.println(tmp.getMsg());

    }
}
  1. ?通配符基础上还有两个子通配符:

· ?extends 类:设置泛型上限,可以在声明上和方法参数上使用;
|- ?extends Number:意味着可以设置Number或者Number的子类(Integer,Double等)
· ?super 类:设置泛型下限,方法参数使用;
|-?super String:意味着只能设置String或者它的父类Object.

范例:设置泛型上限

class Message<T extends Number> {
    private T msg;

    public void setMsg(T msg) {
        this.msg = msg;
    }

    public T getMsg() {
        return msg;
    }

}

public class Demo {
    public static void main(String[] args) {
        Message<Integer> mA = new Message<Integer>();
        mA.setMsg(100);
        fun(mA); // 引用传递
    }

    public static void fun(Message<? extends Number> temp) {
        System.out.println(temp.getMsg());
    }
}

范例:将Integer改为String

Message<String> mA = new Message<String>();

此时设置为非Number或其子类,那就会出现语法错误。
范例:设置泛型下限

class Message<T> {
    private T msg;

    public void setMsg(T msg) {
        this.msg = msg;
    }

    public T getMsg() {
        return msg;
    }

}

public class Demo {
    public static void main(String[] args) {
        Message<String> mA = new Message<String>();
        mA.setMsg("Hello");
        fun(mA); // 引用传递
    }

    public static void fun(Message<? super String> temp) {
        System.out.println(temp.getMsg());
    }
}

泛型接口

1、泛型可以在接口上声明,这样的接口称为泛型接口。
范例:定义泛型接口

// 如果是接口在前面加“I”,例如:IMessage;
// 如果是抽象类前面加Abstract,例如:AbstractMessage
// 如果是普通类直接写,例如:Message
interface IMessage<T> { // 设置泛型接口
    public void print(T t);
}

2、接口必须定义其相应的子类,定义子类有两种形式:
形式一:在子类继续设置泛型

// 子类也继续使用泛型,接口使用和子类一样的泛型标记
class Message<T> implements IMessage<T> {
    public void print(T t) {
        System.out.println(t);
    }
}

public class Demo {
    public static void main(String[] args) {
        IMessage<String> msg = new Message<String>();
        msg.print("Hello");
    }
}

形式二:在子类不设置泛型,但为接口明确定义一个泛型

class Message implements IMessage<String> {
    public void print(String t) {
        System.out.println(t);
    }
}

public class Demo {
    public static void main(String[] args) {
        IMessage<String> msg = new Message();
        msg.print("Hello");
    }
}

泛型方法

泛型方法也可以定义在普通类中。
范例:泛型方法定义

public class Demo {
    public static void main(String[] args) {
        String str = fun("Hello");
        System.out.println(str.length());
    }

    // T的类型由传入的参数类型决定
    public static <T> T fun(T t) {
        return t;
    }
}

总结:
1、泛型解决的是向下转型所带来的安全隐患,其核心是在声明类或接口时不设置参数或属性的类型;
2、“?”可以接收任意的泛型类型,只能取出,不能修改泛型

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值