JavaSE——泛型

1.泛型问题引出

假设需要你定义一个描述坐标的程序类Point,需要提供两个属性x、y。对于这两个属性的内容可能有如下选择:
1.x = 10、y = 20 ;
2. x = 10.1、y = 20.1 ;
3. x = 东经80度、y = 北纬20度
那么现在首先要解决的问题就是Point类中的x、y的属性类型问题,此时需要保存的有int、double、String,所以在java中只有一种类型可以保存所有类型,那就是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 Test{
    public static void main(String[] args) {
        //设置整型坐标
        // 设置数据
        Point p = new Point() ;
        p.setX(10); // 自动装箱并且向上转型为Object
        p.setY(20);
        // 取出数据
        int x = (Integer) p.getX() ; // 强制向下转型为Integer并且自动拆箱
        int y = (Integer) p.getY() ;
        System.out.println("x = " +x+",y = "+y);

        //设置字符串坐标
        // 设置数据
        Point p1 = new Point() ;
        p1.setX("东经80度");
        p1.setY("北纬20度");
        // 取出数据
        String x1 = (String) p1.getX() ;
        String y1 = (String) p1.getY() ;
        System.out.println("x1 = " +x1+",y1 = "+y1);
    }
}
//输出
//x = 10,y = 20
//x1 = 东经80度,y1 = 北纬20度

以上代码看似解决了问题,实际上并没有,如下:
在这里插入图片描述
由于设置方的错误将内容设置成了double和String,于是在执行的时候会出现ClassCaseException,该异常是指两个没有关系的对象进行强转的异常,所以说明,向下转型是不安全操作,会带来隐患

2.泛型类的基本使用

泛型:指的就是在类定义的时候并不会设置类中的属性或方法中的参数的具体类型,而是在类使用时再进行定义,如果要想进行这种泛型的操作,就必须做一个类型标记的声明,语法如下:

class MyClass<T> {
	T value1;
}

尖括号<> 中的 T 被称作是类型参数,用于指代任何类型。实际上这个T你可以任意写,但出于规范,还是建议用单个大写字母来代表类型参数,常见的用法如下:

  • T代表一般的任何类
  • E代表 Element 的意思,或者 Exception 异常的意思
  • K代表 Key 的意思
  • V代表 Value 的意思,通常与 K 一起配合使用
  • S代表 Subtype 的意思

使用泛型类:

MyClass<String> myClass1 = new MyClass<String>();
MyClass<Integer> myClass2 = new MyClass<Integer>();

!!!注意泛型只能接受类,所有的基本数据类型必须使用包装类,另外,泛型类可以接收多个类型参数,如下:

class MyClass<T,E> {
}

public class Test {
    public static void main(String[] args) {
        MyClass<String,Integer> myClass1 = new MyClass<String,Integer>();
    }
}

使用泛型实现上面的Point类:

class Point <T> {
    // T表示参数,是一个占位的标记;如果有多个泛型就继续在后面追加
    private T x ;
    private T 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;
    }
}
public class Test {
    public static void main(String[] args) {
        // 设置数据
        Point<String> p = new Point<>();
        p.setX("东经80度");
        p.setY("北纬20度");
        // 取出数据
        // 避免了向下转型
        String x = p.getX() ;
        String y = p.getY() ;
        System.out.println("x = " +x+",y = "+y);
    }
}
//输出
//x = 东经80度,y = 北纬20度

!!!注意:引入泛型后,如果明确设置了类型,则为设置类型;如果没有设置类型,则默认为Object类型

3.泛型方法

泛型不仅可以用于定义类,还可以单独来定义方法,如下:

class MyClass{
    public <T> void testMethod(T t) {
        System.out.println(t);
    }
}

泛型方法与泛型类稍有不同的地方是,类型参数也就是尖括号那一部分是写在返回值前面的,当然,声明的类型参数,其实也是可以当作返回值的类型的,如下:

class MyClass{
	public <T> T testMethod(T t) {
		return t;
	}
}

泛型方法与泛型类可以共存,如下:

扫描二维码关注公众号,回复: 6494392 查看本文章
class MyClass<T>{
    public void testMethod1(T t) {
        System.out.println(t);
    }
    public <T> T testMethod2(T t) {
        return t;
    }
}
public class Test {
    public static void main(String[] args) {
        MyClass<String> myClass = new MyClass<>();
        myClass.testMethod1("hello 泛型类");
        Integer i = myClass.testMethod2(100);
        System.out.println(i);
    }
}

上面代码中,MyClass 是泛型类,testMethod1 是泛型类中的普通方法,而 testMethod2 是一个泛型方法。而泛型类中的类型参数与泛型方法中的类型参数是没有相应的联系的,泛型方法始终以自己定义的类型参数为准。泛型类的实际类型参数是 String,而传递给泛型方法的类型参数是Integer,两者不相干。实际开发中,如果在一个泛型类中存在泛型方法,那么两者的类型参数最好不要同名,比如MyClass代码可以更改为如下:

class MyClass<T>{
    public void testMethod1(T t) {
        System.out.println(t);
    }
    public <E> E testMethod2(E e) {
        return e;
    }
}

综上:当泛型类与泛型方法共存时,泛型方法中的类型参数与反省类的参数无关,泛型方法始终以自己的类型参数为准,如果在一个泛型类中存在泛型方法,那么两者的类型参数不要同名

4.通配符

在程序类中追加了泛型的定义后,避免了ClassCastException的问题,但是又会产生新的情况:参数的统一问题,如下:

class Message<T> {
    private T message ;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
public class Test {
    public static void main(String[] args) {
        Message<String> message = new Message() ;
        message.setMessage("我是小靓仔");
        fun(message);
    }
    public static void fun(Message<String> temp){
        System.out.println(temp.getMessage());
    }
}
//输出
//我是小靓仔

以上程序会带来新的问题:

public class Test {
    public static void main(String[] args) {
        Message<Integer> message = new Message() ;
        message.setMessage(99);
        fun(message); // 出现错误,只能接收String
    }
    public static void fun(Message<String> temp){
        System.out.println(temp.getMessage());
    }
}

我们需要可以接收所有的泛型类型,但是又不能够让用户随意修改,种情况就需要使用通配符"?",如下:

public class TestDemo {
    public static void main(String[] args) {
        Message<Integer> message = new Message() ;
        message.setMessage(55);
        fun(message);
    }
    public static void fun(Message<?> temp){
        System.out.println(temp.getMessage());
    }
}

在"?"基础上又产生了两个子通配符:

  • ? extends 类设置泛型上限
    ? extends Number,表示只能够设置Number及其子类
  • ? super 类设置泛型下限
    ? super String,表示只能设置String及其父类
class Message<T extends Number> { 
	// 设置泛型上限
    private T message ;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
public class Test {
    public static void main(String[] args) {
        Message<Integer> message = new Message() ;
        message.setMessage(55);
        fun(message);
    }
    // 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改
    public static void fun(Message<? extends Number> temp){
    	//temp.setMessage(100); 仍然无法修改!
        System.out.println(temp.getMessage());
    }
}
class Message<T> {
	//设置泛型下限
    private T message ;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
public class Test {
    public static void main(String[] args) {
        Message<String> message = new Message() ;
        message.setMessage("Hello World");
        fun(message);
    }
    public static void fun(Message<? super String> temp){
    	//此时可以修改
        temp.setMessage("aa!");
        System.out.println(temp.getMessage());
    }
}
//输出
//aa!

方法参数设置泛型上限依旧只能取得类中属性值而无法设定,因为父类不一定能向下转型为子类,但是设置泛型下限不仅可以取得类中属性值,还可以设置属性值,因为子类可以天然向上转型为父类

5.泛型接口

泛型除了可以定义在类中,也可以定义在接口里面,这种情况我们称之为泛型接口,如下:

interface IMessage<T> { 
    // 在接口上定义了泛型
    public void print(T t) ;
}

对于这个接口的实现子类有两种做法:

  • 在子类定义时继续使用泛型,具体使用时给出类型
  • 在子类实现接口的时候明确给出具体类型
//子类定义时继续使用泛型
interface IMessage<T> {
    public void print(T t) ;
}
class MessageImpl<T> implements IMessage<T> {
    @Override
    public void print(T t) {
        System.out.println(t);
    }
}
public class Test {
    public static void main(String[] args) {
        IMessage<String> msg = new MessageImpl() ;
        msg.print("Hello World");
    }
}
//在子类实现接口的时候明确给出具体类型
interface IMessage<T> {
    public void print(T t) ;
}
class MessageImpl implements IMessage<String> {
    @Override
    public void print(String t) {
        System.out.println(t);
    }
}
public class Test {
    public static void main(String[] args) {
        IMessage<String> msg = new MessageImpl() ;
        msg.print("Hello World");
    }
}

6.类型擦除

泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,这就是类型擦除,换句话说,泛型类与普通类在JVM中没有差别泛型类的类型参数如果没有指定类型上限,就会被擦除成Object类,如果指定上限,擦除为相应类型上限

class MyClass<T>{
    private T message;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
    public void testMethod1(T t) {
        System.out.println(t);
    }
}
public class Test {
    public static void main(String[] args) {
        MyClass<String> myClass1 = new MyClass<>();
        MyClass<Integer> myClass2 = new MyClass<>();
        System.out.println(myClass1.getClass() == myClass2.getClass());
    }
}
//输出
//true

打印的结果为 true 是因为 MyClass 和 MyClass 在 jvm 中的 Class 都是 MyClass.class

class MyClass<T,E>{
    private T message;
    private E text;
    public E getText() {
        return text;
    }
    public void setText(E text) {
        this.text = text;
    }
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
    public void testMethod1(T t) {
        System.out.println(t);
    }
}
public class Test {
    public static void main(String[] args) {
        MyClass<String,Integer> myClass1 = new MyClass<>();
        Class cls = myClass1.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getType());
        }
    }
}
//输出
//class java.lang.Object
//class java.lang.Object

猜你喜欢

转载自blog.csdn.net/LiLiLiLaLa/article/details/91482841