浅谈java泛型

做者:hubery, 时间:2018.11.30-12.01java

提及泛型,算是java中逼格较高的技能了吧,往往看到某个开源框架或者技术架构最顶层的接口实现,全是P/Q/R/T等的类型参数,瞬间跪着看。其实不少时候都是一叶障目,打算静下心来研究下,写几个简单的类,稍微一运行感觉下。android

期间查阅了屡次Java编程思想 这本圣经,本人理解的仍是太浅,暂且一看,附上连接。git

连接:pan.baidu.com/s/1GVwEydQp… 密码:kijqgithub

推荐个项目:android-architecture算法

googlesample中的android-architecture项目,里面基本都是Google的各风格的项目,值得一看,其中大量用到了泛型。编程

简单泛型类

public class Main {
    static class Automobile {}

    public static class Holder1 {
        private Automobile a;
        public Holder1(Automobile a) { this.a = a; }
        Automobile get() { return a; }
    }

    public static class Holder2<T> {
        private T a;
        public Holder2(T a) {this.a = a;}

        public void set(T a) { this.a = a; }
        public T get() {return a;}
    }

    public static void main(String[] args) {
        Holder1 h1 = new Holder1(new Automobile());
        Holder2<Automobile> h2 = new Holder2<Automobile>(new Automobile());
    }
}
复制代码

该测试代码中,Holder1是普通类使用,Holder2是用了泛型T。 泛型的主要目的是:用来指定T是什么类型,由编译器来保证T的正确性。 咱们要的是暂时不指定类型,等用到的时候再决定是什么类型。要达到这个效果,须要用类型参数T。用尖括号括住,放在类名后面:Holder2<T>,后续使用该类的时候用实际类型来替换此类型参数T。设计模式

泛型核心:告诉编译器想用什么类型,而后编译器会帮你处理一切细节。 通常来讲,泛型和其余类型差很少,只不过泛型有类型参数而已。 使用泛型时只须要指定泛型的名称和类型参数列表便可。bash

泛型接口

泛型可应用于接口。设计模式中工厂模式的应用:对象生成器。 如:架构

public interface Generator<T> {
    T next();
}
复制代码

写个生成器,next方法返回T。接口与类在使用泛型上,没区别。框架

public class Fibonacci implements Generator<Integer> {

    private int count = 0;

    @Override
    public Integer next() {
        return fib(count++);
    }

    private int fib(int n) {
        if(n < 2) return 1;
        return fib(n - 2) + fib(n - 1);
    }

    public static void main(String[] args) {
        Fibonacci gen = new Fibonacci();
        for (int i = 0; i < 10; i++) {
            System.out.println(gen.next() + " ");
        }
    }
}
复制代码

斐波那契数列数列的应用,实现泛型接口,指定T为Integer型,相应next回调方法返回的T也替换成Integer型。感觉一下,完美。 至于算法方面不作多深究,这里主要用来感觉泛型接口,找找感受。

泛型方法

泛型也能够用在方法上。泛型方法所在的类,能够是泛型类,也能够是普通类,没相关性。 泛型方法指导原则:不管什么时候,只要你能作到,尽可能使用泛型方法。 泛型方法的定义:只需将泛型参数列表放到返回值以前便可。 看代码:

public class GenericsMethods {
	 // 泛型方法:将参数列表写到返回值以前就能够了
    public <T> void f(T x) {
        System.out.println(x.getClass().getName());
    }

    public static void main(String[] args) {
        GenericsMethods gm = new GenericsMethods();
        gm.f("");
        gm.f(20);
        gm.f(1.1);
        gm.f(1.0F);
        gm.f(gm);
    }
}
复制代码

打印结果:

java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
cn.hubery.generic.GenericsMethods
复制代码

代码中,f()拥有类型参数,由该方法的返回类型前面的类型参数列表指明的。

注:

使用泛型类时,建立对象的时候必须指定类型参数的具体类型;</p>
使用泛型方法时,没必要指明参数类型,编译器会自行推断出所需的具体类型;
复制代码

写个通用的Generator 泛型类+泛型方法

public class BasicGenerator<T> implements Generator<T> {

    private Class<T> type;

    public BasicGenerator(Class<T> type) {
        this.type = type;
    }

    @Override
    public T next() {
        try {
          return type.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<T>(type);
    }
}
复制代码

写个辅助对象类:

public class CountedObject {
    private static long counter = 0;
    private final long id = counter++;
    public long id() {
        return id;
    }

    public String toString() {
        return "CountedObject" + id;
    }
}
复制代码

写个测试类:

public class BasicGeneratorTest {

    public static void main(String[] args) {
        Generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(gen.next());
        }
    }
}
复制代码

打印状况:

CountedObject0
CountedObject1
CountedObject2
CountedObject3
CountedObject4
复制代码

经过测试看到,用泛型方法建立Generator对象,大大减小代码量。泛型方法的入参要求传入Class对象,故能够推断出确切类型。仍是得多揣摩下。

匿名内部类

待研究。

边界

能够在泛型的参数类型上设置限制条件。 因为擦除动做会移除类型信息,故用无界泛型参数调用的方法只是那些Object可用的方法。 若是将这个参数限制在某个类型的子集中,那咱们就能够调用这些类型子集的方法。 为了设置这种限制,java的泛型用了extends关键字。 此处的extends与传统意义上的类继承不是一回事儿。 写法是:TestClass<T extends LineClass>

package cn.hubery.generic.line;

import java.util.List;

public class EpicBattle {

    interface SuperPower{}
    interface XRayVision extends SuperPower {
        void seeThroughWalls();
    }

    interface SuperHearing extends SuperPower {
        void hearSubtleNoises();
    }

    interface SuperSmell extends SuperPower {
        void trackBySmell();
    }

    static class SuperHero<POWER extends SuperPower> {
        POWER power;
        SuperHero(POWER power) {
            this.power = power;
        }
        POWER getPower() {
            return power;
        }
    }

    class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> {
        SuperSleuth(POWER power) {
            super(power);
        }
        void see() {
            power.seeThroughWalls();
        }
    }

    static class CanineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER> {
        CanineHero(POWER power) {
            super(power);
        }
        void hear() {
            power.hearSubtleNoises();
        }
        void smell() {
            power.trackBySmell();
        }
    }

    static class SuperHearSmell implements SuperHearing, SuperSmell {

        @Override
        public void hearSubtleNoises() {

        }

        @Override
        public void trackBySmell() {

        }
    }

    static class DogBoy extends CanineHero<SuperHearSmell> {
        DogBoy() {
            super(new SuperHearSmell());
        }
    }

    static <POWER extends SuperHearing> void useSuperHearing(SuperHero<POWER> hero) {
        hero.getPower().hearSubtleNoises();
    }

    static <POWER extends SuperHearing & SuperSmell> void superFind(SuperHero<POWER> hero) {
        hero.getPower().hearSubtleNoises();
        hero.getPower().trackBySmell();
    }

    public static void main(String[] args) {
        DogBoy dogBoy = new DogBoy();
        useSuperHearing(dogBoy);
        superFind(dogBoy);
        List<? extends SuperHearing> audioBoys;
//        List<? extends SuperHearing & SuperSmell> dogBoy;// error
    }
}
复制代码

讲真,有时候 意会不能言传,容我回头组织下语言再慢慢道来。

通配符

泛型参数表达式中的问号 ?

public class TPF {

    static class Fruit{}
    static class Apple extends Fruit{}

    public static void main(String[] args) {
        List<? extends Fruit> flist = Arrays.asList(new Apple());
//        flist.add(new Apple());// 不能随意向flist中添加对象
//        flist.add(new Fruit());
        Apple a = (Apple) flist.get(0);
        flist.contains(new Apple());
        flist.indexOf(new Apple());
    }
}
复制代码

flist类型是List<? extends Fruit>,能够理解问:具备任何从Fruit继承的类型的列表。但,这实际上不表明该List能够持有任何类型的Fruit。 通配符?引用的是明确的类型,所以意味着:某种flist饮用没有指定的具体类型。 例子还没想清楚怎么写,回头补上。

注意事项

基本类型不能做为类型参数T

如:不能建立ArrayList之类的。若是须要,jdk会启用自动包装机制,int->Integer。

实现参数化接口

由于编译器会擦除泛型参数,因此要注意,使用泛型类时尽可能类型不一致。

重载

因为存在擦除,重载方法会产生相同的类型签名,故:保险起见,方法名尽可能不一致。

注: 刚通了个宵,白天睡了个回笼觉,平复心情中,如今整理思绪,关于泛型的理解会慢慢在本文中补充,欢迎拍砖。

刚才看了下,有几位朋友提出:泛型,不是范型。错别字,很是感谢。因为输入法第一个是范型,而后就没深究,也是本身不够严谨,再次感谢几位朋友的指正。

天星技术团QQ:557247785

欢迎来扰
相关文章
相关标签/搜索