Java基础之泛型

介绍

来自《Effective Java》介绍泛型的一段话。在没有泛型之前,从集合中读取到的每一个对象都必须进行转换。如果有人不小心插入了类型错误的对象,在运行时的转换处理就会出错。有了泛型之后,可以告诉编译器每个集合中接受哪些对象类型。编译器自动地为你的插入进行转化,并在编译时告知是否插入了类型错误的对象。

最能表示是Java 1.5版本之前的集合类。集合中每个方法传参都是Object类,毕竟类型不确定。反正Object是上帝类,也符合集合能装载任何Object类的子类。比如下面的示例,编译时不会报错,因为能接收Object类型。不过你遍历集合操作的时候,你就需要一个个强转相应的类型。非常麻烦。

    //1.5版本之前
    List list = new ArrayList();
    list.add(new String());
    list.add(new A());
    list.add(new B());

    String str = (String) list.get(0);
    A a = (A) list.get(1);
    B b = (B) list.get(2);

谁让你把不同的类型放在一个集合里面,实际开发难免会遇到这种问题,传过来List类,但是不知道要里面到底装的是什么类型。后来1.5版本引进了泛型,改进了集合,只允许同类型的进来。如果类型不对,编译时就会提示错误。提高安全性。编译无误,会将泛型擦除。

    1.5版本之后
    List<String> list = new ArrayList<String>();//只允许传String类型
    Map<String,Demo> map = new HashMap<String,Demo>();//只允许传key为String类型,value为Demo类型

泛型类

泛型,其实泛型就是标识,把具体类型抽象化。在类内部的抽象化类型称为类型形参,类外部称为类型实参。跟方法内的形参和方法外的实参的意思一样。使用泛型,首先要声明,泛型类的声明在类名后面。比如下面的示例,有T的地方就可以看做成指定那个类型。

    public class Test {//声明T类型形参
        T t;
    }

    //创建Test对象时,就可以加上泛型
    Test test = new Test();//指明类型实参是String

    //实际上相当于T都换成了String
    public class Test {//声明此类标识泛型
        String t;//指定的类型
    }

为什么是T呢?多个类型形参要怎么做。泛型就是标识,用于取代具体类型。标识就跟变量名一样。要是多个类型形参只要用逗号隔开就好了。

    public class Test<_123,ABC> {//声明多个类型形参
        _123 _1234; 
        //指定ABC类型也不一定要使用
    }

接口相当于类,同样可以使用。用法跟泛型类一样。只要指定类型实参,接口暴露出来也就那类型实参。非常干净。

    public interface ITest {
        void getType(T t);
    }

    new ITest(){//指定类型实参为String

            public void getType(String t) {//返回的类型也为String
            }

        };

泛型方法

泛型不止与用在类,也可以用于方法。别忘了都要声明。还可以标识返回指定那类型,通常用于强转,省下不用在外部加上括号的麻烦。

    public  void printString(T t){
        t.toString();//打印指定类型
    }

    public  T getT(String s){
        return (T) s;//将String s强转T类型并且返回
    }

边界

声明类型形参是解决了表明安全性,但是会发现类型形参跟Object类没任何区别。因此我们可以做限制。可以声明类型形参是某类的子类,或者是某类的父类。extends表上界,super表下界,?表通配符。? extends T叫上界通配符,? super T叫下界通配符。

     ///指定任意类型
    <T extends Light> ///指定类型是Light的子类
    <T super Light> ///指定类型是 Light的父类
    extends Light> ///指定类型是 Light的子类
    super Light> ///指定类型是 Light的父类
    extends T> ///指定类型是 T的子类
    super T> //指定类型是 T的父类

额。这么多可以看着头晕。来分个类吧。在类内部?和super是无法来用声明指定类型。因为编译器根本不知道你指定什么鬼类型。只能确定指定类型是否为某类的子类。只有extends还可以同于声明。

    public class Test<T extends Light>

    //以下编译错误
    public class Test<T super Light>
    public class Test<T super ?>

还可以使用方法中的声明,并且能调用父类的方法。

    public  void test(T t){//表明类型形参T是Light类的子类
        t.getLightValus();//当类型形参的是继承Light类,类型形参就可以调用Light类的方法
    } 

那?和super的作用在哪呢。其实是声明的外面,类的外面。extends在外面也可以使用。

    //已经Test声明T必须为Light的子类时
    Test test = new Test();
    Test<Light> test = new Test<Light>();
    Testextends Light> test = new Test<Light>();

    //以下编译会报错
    Test<A> test = new Test<A>();
    Test test = new Test();

只有声明类型形参的时候还可以使用extends,类型实参三个都可以。掌握之后,可以写出非常优雅的代码。

总的来说,泛型目的就是加强安全性,提升灵活性,简化了代码。写到这里,不足之处望指教。

你可能感兴趣的:(java基础)