泛型

一、泛型产生的原因

①、最主要的目的就是创造容器。

举例:只能持有一个对象的类

public class Holder{
   private Auto mAuto;
  public Holder(Auto auto){
       mAuto = auto;
  }

  public Auto getAuto(){
    return mAuto;
  }
}
Holder

这个类只能持有Auto类型的对象,而不能持有其他类型的对象。缺点:如果想持有其他类型的对象,就需要重新在创建一个。

在没有泛型的解决方法:就是将Auto转换成Object,这样其他对象就可以使用了

缺点:但是Object类说明,什么样的类都可进入容器,但是一般我们只需要存储一种类型,而不是各种各样的类型都可以放进容器中。

所以:泛型的主要作用是,确定容器要持有什么对象,然后通过编译器保证类型的正确性。

举例:

public class SimpeHolder<T> {
    
    private T base;
    
    public void setBase(T base) {
        this.base = base;
    }

    public T getBase() {
        return base;
    }
    
    public static void main(String[]args){
        //说明如果不添加泛型,T就被当成Object
        SimpeHolder simple = new SimpeHolder();
        simple.setBase(new Mocha());
        Object object = simple.getBase();
        //添加泛型的使用
        SimpeHolder<Mocha> mocha = new SimpeHolder<Mocha>();
        mocha.setBase(new Mocha());
        Mocha mo = mocha.getBase();
    }
}
SimpleHolder

小知识:①、静态类型无法使用泛型   ②、不指定泛型,T代表Object

二、元组

产生的原因:以此方法调用能够返回许多个对象。可是return只能返回一个对象

解决方法:创建一个对象,持有想返回的多个对象。(这就是元组)

特性:这个对象允许读取元素,但不能够允许添加或者修改元素。

举例:

public class TwoTuple<BasicClass,BasicClass1>{
    //① 为什么用public final 而不使用private 加上get()方法
    public final BasicClass base1;
    public final BasicClass1 base2;
    public TwoTuple(BasicClass a,BasicClass1 b){
        base1 = a;
        base2 = b;
    }
}
TwoTuple

知识:①、根据功能出发,我们声明private然后提供get()方法,不如使用final,因为final提供了同样的安全性。相比而言优点更加简洁明了。

注意:元组的顺序是根据泛型的顺序确定的。

三、通用的堆栈类

public class ListStack<T> {
    public static class Node<T>{
        public T item;
        public Node next;
        //结合isEnd() 初始化Node为null,然后当到达尾部的时候,就是到达初始化Node,如果为null则表示到达尾部。
        public Node(){
            this.item = null;
            this.next = null;
        }
        public Node(T item,Node next){
            this.item = item;
            this.next = next;
        }
        public boolean isEnd(){
            return item == null && next == null;
        }
    }
    //初始化一个Node,不传值,用来进行是否达到队列尾部的判断
    private Node<T> top = new Node();
    public void push(T item){
        //创建node,并放入顶部
        Node node = new Node(item,top);
        top = node;
    }
    //获取栈顶 
    public T pop(){
        if (!top.isEnd()){
            T item = top.item;
            top = top.next;
            return item;
        }
        return null;
    }
    //返回true代表成功,返回false代表失败
    public boolean remove(){
        if (!top.isEnd()){
            Node oldTop = top;
            top = top.next;
            oldTop.next = null;
            return true;
        }
        return false;
    }
    
    public static void main(String[]args){
        ListStack<BasicClass> stack = new ListStack();
        for(int i=0; i<3; ++i){
            BasicClass item = new BasicClass();
            stack.push(item);
        }
        for(int i=0; i<5; ++i){
            BasicClass data = stack.pop();
            if (data != null)
            System.out.println(data);
        }
    }
}
ListStack

技巧:明白栈的主要方法:入栈和出栈  ②、明白结构体的作用  ③、判定是否到达栈底

四、泛型接口(生成器、适配器)

作用:专门负责生成新的对象。 

与工厂方法的比较:工厂方法一般需要参数  但是  生成器不需要参数。

举例:

新建:多个元素 继承Coffee  比如:Macha,Cappuccio,Breve

新建:生成器Generator<T>

public interface Generator<T> {
    T next();
}
Generator

利用生成器产生随机的Coffee类

public class CoffeeGenerator implements Generator<Coffee>,Iterable<Coffee>{
    //当使用Iterator的时候,进行判定的
    private int size;
    
    private Class [] objects = {Breve.class,Cappuccino.class,Mocha.class};
    //用普通方法生成对象
    public CoffeeGenerator(){}
    //用构造器生成对象
    public CoffeeGenerator(int size){
        this.size = size;
    }
    //生成器
    @Override
    public Coffee next() {
        // TODO Auto-generated method stub
        try {
            return (Coffee)objects[new Random().nextInt(objects.length)].
                    newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
    //定制迭代器
    class CoffeeIterator implements Iterator<Coffee>{
        @Override
        public boolean hasNext() {
            size = size-1;
            return size>0;
        }
        @Override
        public Coffee next() {
            // TODO Auto-generated method stub
            return CoffeeGenerator.this.next();
        }
        
    }
    @Override
    public Iterator<Coffee> iterator() {
        // TODO Auto-generated method stub
        return new CoffeeIterator();
    }
    
    public static void main(String[]args){
        
        //未使用迭代器
        CoffeeGenerator coffee = new CoffeeGenerator();
        for(int i=0; i<5; ++i){
            System.out.println(coffee.next());
        }
    System.out.println("-------------------------------------");
        //使用迭代器
        for(Coffee cof:new CoffeeGenerator(6)){
            System.out.println(cof);
        }
    }
}
CoffeeGenerator

这里用到了迭代器:如何创建迭代器(复习知识)  迭代器也是使用到了泛型,因为要迭代的容器肯定是某一类,而不可能是多种类

让CoffeeGenerator继承 Iterable<>接口:只有声明为Iterable才能使用迭代器。

之后创建Iterator<>迭代器,创建类,继承Iterator接口,并实现

之后将新建的Iterator对象交给Iterable<>  

foreach循环能够执行继承Iterable接口的类

适配器

假设现在有:sequence这个类,这个类的作用是随机创建一个数字
举例:

public class RandomSequence {
    private int data;
    private Random mRandom;
    public RandomSequence(){
        mRandom = new Random();
    }
    public Integer getData(){
        return mRandom.nextInt(100);
    }
    
    public static void main(String[]args){
        RandomSequence random = new RandomSequence();
        for (int i=0; i<50; ++i){
            System.out.println(random.getData());
        }
    }
}
RandomSequence

现在想通过利用foreach循环来创建数字。

但是foreach循环需要是Iterable接口才能使用。

所以为能够使用foreach循环,需要一个接口能够相互转换将Sequence类转换成Iterator类(或者说小明充电,他是苹果机(Squence),现在只有一条android数据线,所以需要一个能够连接android线的苹果插口,能够连接充电宝(foreach))

新建IteratorSequence类 继承Iterabe接口(苹果连接android插口)提供了转化的工具

ublic class IteratorSquence extends RandomSequence implements Iterable<Integer> {
    private int mCount = 0;
    public IteratorSquence(int count){
        mCount = count;
    }
    
    class Squence implements Iterator<Integer>{
        @Override
        public boolean hasNext() {
            // TODO Auto-generated method stub
            return mCount>0;
        }

        @Override
        public Integer next() {
            mCount = mCount + 1;
            // TODO Auto-generated method stub
            return IteratorSquence.this.getData();
        }
        
    }
    public static void main(String[]args){
        for(int data : new IteratorSquence(18)){
            System.out.println(data);
        }
    }
    @Override
    public Iterator<Integer> iterator() {
        // TODO Auto-generated method stub
        return new Squence();
    }
}
IteratorSquence

这就是适配器模式:将一个类,通过适配,能够被另一个类使用。

泛型:Iterable接口使用了泛型,使其能够通用。

五、泛型方法(之前谈论的都是在类上加上泛型)

使用:在返回值前添加泛型参数

举例:

public class GenericMethods {
    //在返回值前面添加参数
    public <T> void f(T data){
        System.out.println(data.getClass().getSimpleName());
    }

    public static void main(String[] args){
        GenericMethods methods = new GenericMethods();
        methods.f("");
        methods.f(12);
    }
}
GenericMethods

特性:参数推断(编译器会为我们找出具体值)

参数推断的作用:

现在我们有Map<String,List<String>> map = new Map<String,List<String>>();这样写的方式太长了

所以利用参数推断:

public class ConcludeElements {
    public static <T,V> Map<T,V> map(){
        return new HashMap<T,V>();
    }
    
    public static void main(String[]args){
        //map()的返回值类型由前面的类型参数确定   (编译器自动进行参数推断)
        Map<String,List<String>> map = ConcludeElements.map();
    
    }
}
ConcludeElements

这样可以减少重复的泛型参数列表:

这还可以用在强制转换上,如果有个方法返回的是Object类型,需要强制转换成其他类型的类的时候,就可以很好的使用到。

比如说android的findViewById():

public class ConcludeElements {
    public <VT extends View> VT getViewById(int data){
        return (VT)findViewById(data);
    }
    
    public static void main(String[]args){
        Button btn = getViewById(R.id.main_btn);
    }
}
AndroidConclude

注意:但是类型参数推断只对赋值有效(因为推断是根据前面的参数决定的,只有赋值才有前面的参数列表)

如果直接使用方法,则参数推断的就是Object类

可变参数与泛型方法

 

posted @ 2016-06-05 17:26  技术丶从积累开始  阅读(229)  评论(0编辑  收藏  举报