Java-泛型

由来

早期的时候,Java使用Object来代表任意的类型。向上转型是没有任何问题的,但是在向下转型的时候其实隐含了类型转换的问题。这样的程序其实并不是安全的。

public class ObjectTool {
    private Object obj;

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }
}

public class ObjectToolDemo {
    public static void main(String[] args) {
        ObjectTool ot = new ObjectTool();

        // 正常使用
        ot.setObj(new Integer(27));
        Integer i = (Integer) ot.getObj();
        System.out.println("年龄是:" + i);

        ot.setObj(new String("jack"));
        String s = (String) ot.getObj();
        System.out.println("姓名是:" + s);

        System.out.println("---------");
        ot.setObj(new Integer(30));
        // ClassCastException
        String ss = (String) ot.getObj();
        System.out.println("姓名是:" + ss);
    }
}

代码 29 行,报错"java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String",因为写入的是 Integer 类型,而读取用 String 类型向下转型,所以在运行期间报错

能不能在写入的时候动态明确类型,读取的时候不需要强制转换直接使用呢?

Java引入了泛型

Java 在 JDK5 后引入了泛型,泛型其实指的就是参数化类型,使得代码可以适应多种类型。比如 Java 容器中(public class ArrayList<E> extends AbstractList<E>),就大量使用泛型,用来指定容器要持有什么类型的对象。而且,可以在编译期明确参数类型防止运行期转换异常的问题

//泛型类:把泛型定义在类上
public class ObjectTool<T> {
	private T obj;

	public T getObj() {
		return obj;
	}

	public void setObj(T obj) {
		this.obj = obj;
	}
}

public class ObjectToolDemo {
    public static void main(String[] args) {

        ObjectTool<String> ot = new ObjectTool<String>();
        // ot.setObj(new Integer(27)); //这个时候编译期间就过不去
        ot.setObj(new String("jack"));
        System.out.println("姓名是:" + ot.getObj());

        ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
        // ot2.setObj(new String("jack"));//这个时候编译期间就过不去
        ot2.setObj(new Integer(27));
        System.out.println("年龄是:" + ot2.getObj());
    }
}

使用泛型类,可以在创建 ObjectTool<String> ot = new ObjectTool<String>(); 明确类型String。插入其他类型数据 ot.setObj(new Integer(27)); 编译期间就会报错。读取数据 ot.getObj()也不需要强制转换(向下转型)。

概述

泛型是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。

格式:<数据类型> 此处的数据类型只能是引用类型

因为集合可以存储多种数据类型的引用,而用的时候需要进行强制转换。如果只进行单一强制类型转换,就会出错。而泛型可以在编译期间指定类型,就避免运行时报错。而且,可以避免强制转换

  • 优点
    • 编译期间指定类型,保证了类型安全
    • 消除了强制类型转换
    • 提高了代码的通用性
    • 提高了性能(其实没有…,因为编译泛型时会自动加入类型转换的编码。但是,运行期间的性能确实提高了:) )
public class GenericDemo {
    public static void main(String[] args) {
        // 创建
        ArrayList<String> array = new ArrayList<String>();

        // 添加元素
        array.add("hello");
        array.add("world");
        array.add("java");

        // 遍历
        Iterator<String> it = array.iterator();
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
    }
}

输出:
hello
world
java

Iterator<String> it = array.iterator(); 指定类型后,it.next(); 就不需要强制转换。

分类

泛型类

public class Generic<A, B, C> {
    private A a;
    private B b;
    private C c;

    public Generic(A a, B b, C c){
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public String toString(){
        return "(" + a + "." + b + "." + c + ")";
    }
}

Class<T>也是泛型类。T 代表一个类型 ,而 Class<T> 代表这个类型所对应的类, Class<?> 表示类型不确定的类。

public class ObjectTool<T> {

    public Class<T> test (Class<T> t){
        System.out.println(t);
        return t;
    }
}

public class ObjectToolDemo {
    public static void main(String[] args) {

        ObjectTool<String> ot = new ObjectTool<String>();
        //Class<Integer> test = ot.test(String.class);   编译报错
        Class<String> test = ot.test(String.class);
        System.out.println(test);
    }
}

输出:
class java.lang.String
class java.lang.String

泛型接口

把泛型定义在接口上

public interface Inter<T> {
    public abstract void show(T t);
}

1、实现类知道该是什么类型的。

public class InterImpl implements Inter<String> {

    @Override
    public void show(String t) {
        System.out.println(t);
    }
}

2、实现类如果还不知道是什么类型的,可以沿用接口泛型。

public class InterImpl<T> implements Inter<T> {

    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

泛型方法

泛型方法有一个基本原则,尽量使用泛型方法而不是泛型类。只要能够用泛型方法达到需求,就应该只用泛型方法,这样能使得代码更加清晰。

格式:泛型参数列表置于返回值前,例如 public <T> void f(T t),返回值也可以使用泛型,public <T> T f(T t)

public class ObjectTool {
    private Object obj;

    public <T> T getObj() {
        return (T)obj;
    }

    public <T> void setObj(T t) {
        this.obj = t;
        System.out.println(t);
    }
}

public class ObjectToolDemo {
    public static void main(String[] args) {
        ObjectTool ot = new ObjectTool();

        ot.setObj(new String("lili"));
        String s = ot.getObj();
        System.out.println("姓名是:" +  ot.getObj());

        System.out.println("---------");
        ot.setObj(100);
        Integer ss =  ot.getObj();
        System.out.println("年龄是:" + ss);
    }
}

输出:
lili
姓名是:lili
---------
100
年龄是:100

小结

本篇介绍了泛型的由来、概述,说明了泛型带来的好处。介绍了泛型类、泛型接口、泛型方法的用法。但是,泛型也消除了类型,下篇将介绍泛型的缺点以及解决方案。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会叫的狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值