1、接口与类型信息
interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合性。
javap -private C 可以反编译显示出这个类的所有方法,无论private还是匿名
2、泛型
实现了参数化类型的概念,使代码可以应用于多种类型。术语的意思为:“适用于许多许多的类型”。最初的目的是希望类或方法能够具备最广泛的表达能力,通过解耦类或方法与所使用的类型之间的约束。
泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。
核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
3、一个元组类库
元组:将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象(也被称为数据传送对象或信使)。
元组可以具有任意长度,对象可以是任意不同的类型,但是希望可以指明其类型。
不用LinkedList实现内部链式存储机制
public class ContainerTest01 {
public static void main(String[] args) {
LinkedStack<String> lss = new LinkedStack<>();
for(String s : "hello world".split(" ")){
lss.push(s);
}
String s;
while((s = lss.pop()) !=null){
System.out.println(s);
}
}
}
class LinkedStack<T> {
@AllArgsConstructor
@Data
@NoArgsConstructor
private static class Node<U> {
U item;
Node<U> next;
boolean end() {
return item == null && next == null;
}
}
private Node<T> top = new Node<T>();
public void push(T item){
top = new Node<T>(item,top);
}
public T pop(){
T result = top.item;
if(! top.end()){
top = top.next;
}
return result;
}
}
使用了末端哨兵(end sentinel)来判断堆栈何时为空。在构造LinkedStack的时候创建的空Node就是末端哨兵。之后,每调用一次push方法,就会创建一个Node对象,并将其链接到前一个Node对象。当调用pop方法时,返回top.item并丢弃大当前top所指向的Node,并将top转译到下一个Node,除非碰到了末端哨兵,这时候不再移动top。当到了末端,继续调用pop只会得到null,说明堆栈为空。
4、泛型接口
泛型应用于接口,生成器:专门负责创建对象的类。是工厂方法设计模式的一种应用,不过使用生成器创建新的对象时,不需要参数,无需额外的信息就知道如何创建新对象。
一般,一个生成器只定义一个方法,用以产生新的对象。
public interface Generator<T> {
T next();
}
生成的类
public class Coffee {
static long counter = 0;
final long id = counter++;
@Override
public String toString() {
return getClass().getSimpleName() + " " + id;
}
}
class Latte extends Coffee {
}
class Mocha extends Coffee {
}
class Cappuccino extends Coffee {
}
class Breve extends Coffee {
}
实现Generator<Coffee>接口来随机生成不同类型的Coffee对象
@NoArgsConstructor
class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
private Class[] types = {Latte.class, Mocha.class, Cappuccino.class, Breve.class};
private static Random rand = new Random(47);
private int size = 0;
public CoffeeGenerator(int sz) {
size = sz;
}
@Override
public Coffee next() {
try {
return (Coffee) types[rand.nextInt(types.length)].newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
class CoffeeIterator implements Iterator<Coffee> {
int count = size;
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public Coffee next() {
count--;
return CoffeeGenerator.this.next();
}
@Override
public void remove(){
throw new UnsupportedOperationException();
}
}
@Override
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
public static void main(String[] args) {
CoffeeGenerator coffees = new CoffeeGenerator();
for (int i = 0; i < 4; i++) {
System.out.println(coffees.next());
}
for (Coffee c : new CoffeeGenerator(4)) {
System.out.println(c);
}
}
}
参数化的Generator接口确保next()返回值是参数的类型,同时还是先了Iterable接口,让他可以在循环语句中用,还有个size来判断何时停止。
public class Fibonacci implements Generator<Integer> {
private int count = 0;
@Override
public Integer next() {
return fib(count++);
}
public int fib(int n) {
if (n < 2) {
return 1;
}
return fib(n - 2) + fib(n - 1);
}
public static void main(String[] args) {
Fibonacci fibonacci = new Fibonacci();
for (int i = 0; i < 18; i++) {
System.out.println(fibonacci.next() + "");
}
}
}
内部使用的int但是参数却是Integer,Java泛型有一个局限性:基本类型无法作为类型参数。
如果想要一个实现了Iterable的Fibonacci生成器,我们可以重写这个类,但是不一定总有权限,因此,我们可以选择创建一个适配器来实现所需接口。
现在我们通过继承来创建适配器类。
class IterableFibonacci extends Fibonacci implements Iterable<Integer> {
private int n;
public IterableFibonacci(int count) {
n = count;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
@Override
public boolean hasNext() {
return n > 0;
}
@Override
public Integer next() {
n--;
return IterableFibonacci.this.next();
}
};
}
}
5、泛型方法
泛型方法使得该方法能够独立于类而产生变化。基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。如果使用泛型方法可以取代将整个类泛型化,那么就应该使用泛型方法,因为可以使事情更清楚明白。
当使用泛型类时,必须在创建对象时指定类型参数的值,而使用泛型方法时,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断(type argument inference)。因此,我们可以调用普通方法一样调用泛型方法,就好像泛型方法被无限次重载过。
6、泛型的匿名内部类
@NoArgsConstructor
class Customer {
private static long counter = 1;
final long id = counter++;
public static Generator<Customer> generator() {
return new Generator<Customer>() {
@Override
public Customer next() {
return new Customer();
}
};
}
@Override
public String toString() {
return "customer:" + id;
}
}
@NoArgsConstructor
class Teller {
static long counter = 1;
final long id = counter++;
public static Generator<Teller> generator() {
return new Generator<Teller>() {
@Override
public Teller next() {
return new Teller();
}
};
}
@Override
public String toString() {
return "Teller:" + id;
}
}
public class BankTeller {
static void server(Teller t, Customer c) {
System.out.println(t + " server:" + c);
}
public static void main(String[] args) {
Random random = new Random(47);
Queue<Customer> customers = new LinkedList<>();
Generators.fill(customers, Customer.generator(), 15);
ArrayList<Teller> tellers = new ArrayList<>();
Generators.fill(tellers, Teller.generator(), 4);
for (Customer c : customers) {
server(tellers.get(random.nextInt(tellers.size())), c);
}
}
}
class Generators {
public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen, int n) {
for (int i = 0; i < n; i++) {
coll.add(gen.next());
}
return coll;
}
}
Custonmer和Teller类都只有private的构造器,可以强制我们必须使用Generator对象。Customer有一个generator方法,每次执行都会生成一个新的Generator<Customer>对象,我们其实不是很需要多个,Teller就职创建了一个public的generator对象。
Thinking In Java Part09(泛型类、泛型方法)
最新推荐文章于 2020-06-16 16:52:31 发布