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)。因此,我們可以調用普通方法一樣調用泛型方法,就好像泛型方法被無限次重載過。
Thinking In Java Part09(泛型類、泛型方法)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.