泛型
一、泛型产生的原因
①、最主要的目的就是创造容器。
举例:只能持有一个对象的类
public class Holder{ private Auto mAuto; public Holder(Auto auto){ mAuto = auto; } public Auto getAuto(){ return mAuto; } }
这个类只能持有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(); } }
小知识:①、静态类型无法使用泛型 ②、不指定泛型,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; } }
知识:①、根据功能出发,我们声明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); } } }
技巧:明白栈的主要方法:入栈和出栈 ②、明白结构体的作用 ③、判定是否到达栈底
四、泛型接口(生成器、适配器)
作用:专门负责生成新的对象。
与工厂方法的比较:工厂方法一般需要参数 但是 生成器不需要参数。
举例:
新建:多个元素 继承Coffee 比如:Macha,Cappuccio,Breve
新建:生成器Generator<T>
public interface Generator<T> { T next(); }
利用生成器产生随机的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继承 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()); } } }
现在想通过利用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(); } }
这就是适配器模式:将一个类,通过适配,能够被另一个类使用。
泛型: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); } }
特性:参数推断(编译器会为我们找出具体值)
参数推断的作用:
现在我们有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(); } }
这样可以减少重复的泛型参数列表:
这还可以用在强制转换上,如果有个方法返回的是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); } }
注意:但是类型参数推断只对赋值有效(因为推断是根据前面的参数决定的,只有赋值才有前面的参数列表)
如果直接使用方法,则参数推断的就是Object类
可变参数与泛型方法