2,簡單泛型
一個元組類庫
return只能返回一個對象,要是想返回多個對象,解決方法就是讓返回的這個對象持有多個想返回的對象,但是每次返回都需要不同類型的對象,那就需要創建很多類,使用泛型可以很好地避免此類問題。同時,我們在編譯時就可以確保類型安全。
這種概念就成爲元組。這個容器對象允許讀取對象,但是允許向其中添加對象。
package com.generic;
public class TwoTuple<A,B> {
public final A first;
public final B second;
public TwoTuple(A a,B b){
first = a;
second = b;
}
public String toString(){
return "("+first+ " "+second+")";
}
}
這裏變量設置爲Final,可以保證其不被修改。
可以使用繼承機制實現長度更長的元組:
package com.generic;
public class ThreeTuple<A,B,C> extends TwoTuple<A, B> {
public final C third;
public ThreeTuple(A a,B b,C c){
super(a,b);
third = c;
}
public String toString(){
return "("+first+ " "+second+" "+third+")";
}
}
實現更長的元祖以此類推。
使用元組:
package com.generic;
class Amp{}
public class TupleTest {
static TwoTuple<String,Integer> f(){
return new TwoTuple<String,Integer>("hi",47);
}
static ThreeTuple<Amp,String,Integer> g(){
return new ThreeTuple(new Amp(),"three",48);
}
public static void main(String[] args) {
System.out.println(f());
System.out.println(g());
}
}
一個堆棧類
不用Linkedlist的Stack:
package com.generic;
public class LinkedStack<T> {
private static class Node<U>{
U item;
Node<U> next;
Node(){
item = null;
next = null;
}
Node(U item, Node<U> next){
this.item = item;
this.next = 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;
}
public static void main(String[] args) {
LinkedStack<String> lss = new LinkedStack<>();
for(String s:"Phasers on stun!".split(" ")){
lss.push(s);
}
String s;
while((s = lss.pop()) != null){
System.out.println(s);
}
}
}
/*
輸出:
stun!
on
Phasers
*/
很有意思的例子。
個人理解相當於一個鏈表的樣子。
RandomList
一個使用泛型實現隨機訪問的例子:
package com.generic;
import java.util.ArrayList;
import java.util.Random;
public class RandomList<T> {
private ArrayList<T> storage = new ArrayList<T>();
private Random rand = new Random(47);
public void add(T item){
storage.add(item);
}
public T select(){
return storage.get(rand.nextInt(storage.size()));
}
public static void main(String[] args) {
RandomList<String> rs = new RandomList<>();
for(String s:"the quick brown fox jumped over".split(" ")){
rs.add(s);
}
for(int i = 0; i < 5; i++){
System.out.print(rs.select()+" ");
}
}
}
/*
輸出:
brown over quick over quick
*/
3,泛型接口
package com.generic;
public interface generator<T> {
T next() throws InstantiationException, IllegalAccessException;
}
定義一些Coffee類:
package com.generic;
public class Coffee {
private static long counter = 9;
private final long id = counter++;
public String toString(){
return getClass().getSimpleName();
}
}
public class Latte extends Coffee{}
....
編寫類,實現這個接口,隨機生成不同類型的Coffee對象:
package com.generic;
import java.util.*;
public class CoffeeGenerator implements generator<Coffee>,Iterable<Coffee>{
private Class[] types = {Latte.class,Mocha.class,Cappuccino.class,Americano.class,Breve.class};
private static Random rand = new Random(47);
public CoffeeGenerator(){}
private int size = 0;
public CoffeeGenerator(int sz){
size = sz;
}
@Override
public Coffee next() throws InstantiationException, IllegalAccessException {
return (Coffee)types[rand.nextInt(types.length)].newInstance();
}
class CoffeeIterator implements Iterator<Coffee>{
int count = size;
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public Coffee next() {
count--;
Coffee coffee = null;
try {
coffee = CoffeeGenerator.this.next();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return coffee;
}
}
@Override
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException{
CoffeeGenerator gen = new CoffeeGenerator();
for(int i = 0; i < 5; i++){
System.out.print(gen.next()+" ");
}
System.out.println();
for(Coffee c: new CoffeeGenerator(5)){
System.out.print(c+" ");
}
}
}
/*
輸出:
Americano Latte Americano Mocha Mocha
Breve Americano Latte Cappuccino Cappuccino
*/
接口泛型的另一個實例:
package com.generic;
import java.util.Iterator;
public class Fibonacci implements generator<Integer>,Iterable<Integer>{
private int count = 0;
private int n = 0;
public Fibonacci(){}
public Fibonacci(int n){
this.n = n;
}
@Override
public Integer next() throws InstantiationException, IllegalAccessException {
return fib(count++);
}
private int fib(int n){
if(n < 2){
return 1;
}
return fib(n-2)+fib(n-1);
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>(){
@Override
public boolean hasNext() {
return n > 0;
}
@Override
public Integer next() {
n--;
int i = 0;
try {
i = Fibonacci.this.next();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return i;
}
};
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException{
Fibonacci gen = new Fibonacci();
for(int i = 0;i < 18;i++){
System.out.print(gen.next()+" ");
}
System.out.println();
for(int i: new Fibonacci(18)){
System.out.print(i+" ");
}
}
}
/*
輸出:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
*/
4,泛型方法
package com.generic;
public class GenericMethods {
public static <T> void f(T x){
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1.0);
gm.f(1.0f);
gm.f('c');
gm.f(gm);
f("");
}
}
泛型方法可以不依賴與泛型類,如果使用泛型方法可以取代泛型類,那就儘量使用泛型方法!
這種利用參數判斷泛型類型。下面使用返回值判斷泛型類型。
類型參數推斷
package com.generic;
import java.util.*;
public class New {
public static <K,V> Map<K,V> map(){
return new HashMap<K,V>();
}
public static <T> void list(){
}
public static <T> LinkedList<T> lList(){
return new LinkedList<T>();
}
public static <T> Set<T> set(){
return new HashSet<T>();
}
public static <T> Queue<T> queue(){
return new LinkedList<T>();
}
public static void main(String[] args) {
Map<String,List<String>> sls = New.map();
New.list();
LinkedList<String> lls = New.lList();
Set<String> ss = New.set();
Queue<String> qs = New.queue();
}
}
但是這種方法只在賦值操作時有效,但在其他地方並不適用,看如下示例:
package com.generic;
import java.util.*;
public class LimitsOfInference {
static void f(Map<Coffee,Coffee> coffee){
System.out.println(coffee.getClass());
coffee.put(new Coffee(), new Latte());
}
public static void main(String[] args) {
f(New.map());
}
}
這段代碼書上說不可以,但是實際是可以的!
可以顯示的指明類型:
package com.generic;
import java.util.Map;
public class ExplicitTypeSpecification {
static void f(Map<Coffee,Coffee> coffee){
System.out.println(coffee.getClass());
coffee.put(new Coffee(), new Latte());
}
public static void main(String[] args) {
f(New.<Coffee,Coffee>map());
}
}
這種必須在方法前指明調用者,在方法前指明泛型類型,這種方法不常用。
可變參數與泛型方法
package com.generic;
import java.util.*;
public class GenericVarargs {
public static <T> List<T> makeList(T...args){
List<T> result = new ArrayList<T>();
for(T item:args){
result.add(item);
}
return result;
}
public static void main(String[] args) {
List<String> ls = makeList("A");
System.out.println(ls);
ls = makeList("A","B","C");
System.out.println(ls);
}
}
用於Generator的泛型方法
就是一個使用泛型的例子:
package com.generic;
import java.util.*;
public class Generators {
public static <T> Collection<T>
fill(Collection<T> coll,generator<T> gen,int n) throws InstantiationException, IllegalAccessException{
for(int i = 0; i < n;i++){
coll.add(gen.next());
}
return coll;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Collection<Coffee> coffee = fill(new ArrayList<Coffee>(),new CoffeeGenerator(),4);
for(Coffee c:coffee){
System.out.println(c);
}
}
}
一個通用的Generator
package com.generic;
public class BasicGeneratorDemo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
for(int i = 0; i < 5; i++){
System.out.println(gen.next());
}
}
}
package com.generic;
public class CountedObject {
private static long counter = 0;
private final long id = counter++;
public long id(){
return id;
}
public String toString(){
return "CountedObject"+id;
}
}
package com.generic;
public class BasicGeneratorDemo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
for(int i = 0; i < 5; i++){
System.out.println(gen.next());
}
}
}
挺簡單的,不解釋了。
簡化元組的使用
package com.generic;
public class Tuple {
public static <A,B> TwoTuple<A,B> tuple(A a, B b){
return new TwoTuple<A,B>(a,b);
}
public static <A,B,C> ThreeTuple<A,B,C> tuple(A a, B b,C c){
return new ThreeTuple<A,B,C>(a,b,c);
}
}
package com.generic;
import static com.generic.Tuple.*;
public class TupleTest2 {
static TwoTuple<String,Integer> f(){
return tuple("hi",47);
}
static TwoTuple f2(){
return tuple("hi",47);
}
static ThreeTuple<Amphibian,String,Integer> h(){
return tuple(new Amphibian(),"hi",47);
}
public static void main(String[] args) {
System.out.println(f());
System.out.println(f2());
System.out.println(h());
}
}
一個Set實用工具
有一個例子,略!
5,匿名內部類
泛型的一個好處就是可以簡單而安全的創建複雜的模型,下面實例很容易的創建了List元組:
package com.generic;
import java.util.*;
public class TupleList<A,B,C> extends ArrayList<ThreeTuple<A,B,C>> {
public static void main(String[] args){
TupleList<Amphibian,String,Integer> t1 = new TupleList<>();
t1.add(TupleTest.g());
t1.add(TupleTest.g());
for(ThreeTuple<Amphibian,String,Integer> i : t1){
System.out.println(i);
}
}
}
另一個複雜模型,真夠複雜的,有點搞不懂:
package com.generic;
import java.util.*;
class Product{
private final int id;
private String description;
private double price;
public Product(int IDumber,String descr,double price){
id = IDumber;
description = descr;
this.price = price;
System.out.println(toString());
}
public String toString(){
return id+":"+description+".price:$"+price;
}
public void priceChange(double change){
price += change;
}
public static generator<Product> generator = new generator<Product>(){
private Random rand = new Random(47);
public Product next(){
return new Product(rand.nextInt(1000),"Test",
Math.round(rand.nextDouble()*1000.0)+0.99);
}
};
}
class Shelf extends ArrayList<Product>{
public Shelf(int nProducts){
try {
Generators.fill(this, Product.generator, nProducts);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Aisle extends ArrayList<Shelf>{
public Aisle(int nShelves,int nProducts){
for(int i = 0; i < nShelves;i++){
add(new Shelf(nProducts));
}
}
}
class CheckoutStand{}
class Office{}
public class Store extends ArrayList<Aisle>{
private ArrayList<CheckoutStand> checkouts = new ArrayList<CheckoutStand>();
private Office office = new Office();
public Store(int nAisles, int nShelves,int nProducts){
for(int i = 0; i < nAisles;i++){
add(new Aisle(nShelves,nProducts));
}
}
public String toString(){
StringBuilder result = new StringBuilder();
for(Aisle a : this){
for(Shelf s: a){
for(Product p : s){
result.append(p);
result.append("\n");
}
}
}
return result.toString();
}
public static void main(String[] args){
System.out.println(new Store(14,5,10));
}
}
邏輯有點複雜,智商不夠用。
7,擦除的神祕之處
儘管可以聲明ArrayList.class
但是不能聲明ArrayList<Integer>
package com.generic;
import java.util.*;
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}
/*
輸出:
true
*/
利用反射得到泛型信息:
package com.generic;
import java.util.*;
class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Particle<POSITION,MOMENTUM>{}
public class LostInformation {
public static void main(String[] args) {
List<Frob> list = new ArrayList<>();
Map<Frob,Fnorkle> map = new HashMap<>();
Quark<Fnorkle> quark = new Quark<>();
Particle<Long,Double> p = new Particle<>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));
System.out.println(Arrays.toString(p.getClass().getTypeParameters()));
}
}
/*
輸出:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
*/
輸出的只是用作參數的佔位符的表示符。
因此,殘酷的現實是:
在泛型代碼內部,無法獲得任何有關泛型參數類型的信息
java泛型是使用擦除來實現的,這意味着使用泛型時,任何具體的類型信息都被擦除了!
C++的方式
看不懂,不看
遷移兼容性
好多字,大體的意思就是泛型不是在java出現時就有的特性,而是後來添加的。爲了不使之前的類庫代碼報廢,只好使用了向前兼容的方式,所有使用了擦除的方式。
擦除的問題
擦除的代價是顯著的,泛型不能用於顯示的引用運行時類型的操作之中,例如轉型,instanceof,和new表達式。因爲所有關於參數的類型的信息都丟失了,所以無論何時,在編寫泛型代碼時,必須時刻提醒自己,這只是看起來擁有有關參數的類型信息而已,因此,如果看到如下代碼:
class Foo<T>{
T var;
}
創建實例時:
Foo<Cat> f = new Foo<Cat>();
Foo類的代碼應該知道工作在Cat類之上,而泛型語法也強烈暗示,在整個類的所有地方,類型T都被Cat替換。但是事實並非如此,當在編寫這個類的代碼時,必須提醒自己:“這只是一個Object”;
所以,擦除和遷移兼容性以爲這使用泛型並不是強制的,不使用泛型只會出現警告而不是報錯!
package com.generic;
class GenericBase<T>{
private T element;
public void set(T arg){
element = arg;
System.out.println("set");
}
public T get(){
return element;
}
}
class Derived1<T> extends GenericBase<T>{}
class Derived2 extends GenericBase{}//不警告
public class ErasureAndInheritance {
public static void main(String[] args) {
Derived2 d2 = new Derived2();
Object obj = d2.get();
d2.set(obj);//警告
}
}
邊界處的動作
正式有了擦除,泛型可以表示沒有任何意義的實物:
package com.generic;
import java.lang.reflect.Array;
import java.util.Arrays;
public class ArrayMaker<T> {
private Class<T> kind;
public ArrayMaker(Class<T> kind){
this.kind = kind;
}
T[] create(int size){
//T[] t = new T[]{kind.newInstance(),kind.newInstance()};報錯
return (T[])Array.newInstance(kind, size);
}
public static void main(String[] args) {
ArrayMaker<String> stringMaker = new ArrayMaker<>(String.class);
String[] stringArray = stringMaker.create(9);
System.out.println(Arrays.toString(stringArray));
}
}
/*
輸出:
[null, null, null, null, null, null, null, null, null]
*/
注意,在泛型中,推薦使用Array.newInstance()。
創建一個容器而不是一個數組:
package com.generic;
import java.util.*;
public class FilledListMaker<T> {
List<T> create(T t, int n){
List<T> result = new ArrayList<T>();
for(int i = 0; i <n;i++){
result.add(t);
}
return result;
}
public static void main(String[] args) {
FilledListMaker<String> stringMaker = new FilledListMaker<>();
List<String> list = stringMaker.create("Hello", 4);
System.out.println(list);
}
}
擦除的補償
擦除丟失了在泛型代碼中執行某些操作的能力。任何在運行時需要知道確切類型信息的操作都無法總做!
package com.generic;
public class Erased<T> {
private final int SIZE = 100;
public void f(Object arg){
/*if(arg instanceof T){
}
T var = new T();
T[] array = new T[SIZE];*///報錯
T[] array = (T[])new Object[SIZE];//警告
}
}
繞過這些問題來編程,可以使用Class:
package com.generic;
class Building{}
class House extends Building{}
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind){
this.kind = kind;
}
public boolean f(Object arg){
return kind.isInstance(arg);
}
public static void main(String[] args) {
ClassTypeCapture<Building> ctt = new ClassTypeCapture<>(Building.class);
System.out.println(ctt.f(new Building()));
System.out.println(ctt.f(new House()));
ClassTypeCapture<House> ctt2 = new ClassTypeCapture<>(House.class);
System.out.println(ctt2.f(new Building()));
System.out.println(ctt2.f(new House()));
}
}
這樣,可以確保類型標籤可以匹配泛型參數。
個人理解:
泛型 T 只可以用來標識另一種東西,如容器類型,返回值類型,參數類型等,但不可以像類一樣生成對象!即不能用在運行時。
new T[]這樣的操作無法實現部分原因是因爲擦除,而另一部分原因是因爲編譯器不能檢查T具有默認構造器這裏寫代碼片
,下面代碼將解決這一問題:
package com.generic;
class ClassAsFactory<T>{
public T x;
public ClassAsFactory(Class<T> kind){
try {
x = kind.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Employee{
public void print(){
System.out.println("Employee");
}
}
public class InstantiateGenericType {
public static void main(String[] args) {
ClassAsFactory<Employee> fe = new ClassAsFactory<>(Employee.class);
fe.x.print();
ClassAsFactory<Integer> in = new ClassAsFactory<>(Integer.class);
}
}
可以看到生成ClassAsFactory<Integer>
實例時會報錯,因爲Integer沒有默認的構造器。同時,這個錯誤不是在編譯期捕獲的。
使用顯示的工廠方法避免運行時異常,個人認爲這種方法不太靈活。
package com.generic;
interface Factory<T>{
T create();
}
class Foo2<T>{
private T x;
public <F extends Factory<T>> Foo2(F factory){
x = factory.create();
}
}
class IntegerFactory implements Factory<Integer>{
public Integer create(){
return new Integer(0);
}
}
class Widght{
public static class FactoryDemo implements Factory<Widght>{
public Widght create(){
return new Widght();
}
}
}
public class FactoryConstraint {
public static void main(String[] args) {
new Foo2<Integer>(new IntegerFactory());
new Foo2<Widght>(new Widght.FactoryDemo());
}
}
使用模板方法模式也能解決這個問題:
package com.generic;
abstract class GenericWithCreate<T>{
final T ele;
GenericWithCreate(){
ele = create();
}
abstract T create();
}
class X{}
class Creator extends GenericWithCreate<X>{
X create(){
return new X();
}
void f(){
System.out.println(ele.getClass().getSimpleName());
}
}
public class CreatorGeneric {
public static void main(String[] args) {
Creator creator = new Creator();
creator.f();
}
}
泛型數組
如前面所示,不能創建泛型數組一般的解決檔案是使用ArrayList:
package com.generic;
import java.util.*;
public class ArrayOfGenericReference<T> {
private List<T> array = new ArrayList<T>();
public void add(T item){
array.add(item);
}
public T get(int index){
return array.get(index);
}
}
package com.generic;
import java.lang.reflect.Array;
class Generic<T>{}
public class ArrayOfGeneric {
static final int SIZE = 5;
static Generic<Integer>[] gia;
public static void main(String[] args) {
gia = (Generic<Integer>[])new Generic[SIZE];
System.out.println(gia.getClass().getSimpleName());
gia[0] = new Generic<Integer>();
//gia[1] = new Generic<Double>();報錯
for(int i = 0; i < SIZE; i++){
System.out.println(gia[i]);
}
}
}
挺正常的代碼。