java基礎總結 --- 泛型 擦除、邊界、萬用字元、
阿新 • • 發佈:2018-12-26
* 擦除的問題 * 為什麼要擦除: 1.5版本才出現泛型 為了相容之前地程式碼 * 它使得泛化的客戶端可以用非泛化的類庫來使用。 * 以及不破壞現有類庫的情況下,將泛型融入java語言。 * 擦除使得現有的非泛型客戶端程式碼能夠在不改變的情況繼續使用,直至客戶端準備號用泛型重寫這些程式碼。 * 擦除的代價:泛型不能用於顯示地引用執行時型別的操作值中,例如轉型instanceof操作和new表示式。 * 因為所有關於引數的型別都丟失了。無論何時,當你在編寫泛型程式碼時,必須時刻提醒自己, * 你只是看起來好像擁有關於引數的型別資訊而已。提醒自己:T 不,它值時一個Object * 擦除使用泛型並不是強制的
package com.zghw.base.generic; import java.util.*; /** * 擦除的問題 * 為什麼要擦除: 1.5版本才出現泛型 為了相容之前地程式碼 * 它使得泛化的客戶端可以用非泛化的類庫來使用。 * 以及不破壞現有類庫的情況下,將泛型融入java語言。 * 擦除使得現有的非泛型客戶端程式碼能夠在不改變的情況繼續使用,直至客戶端準備號用泛型重寫這些程式碼。 * 擦除的代價:泛型不能用於顯示地引用執行時型別的操作值中,例如轉型instanceof操作和new表示式。 * 因為所有關於引數的型別都丟失了。無論何時,當你在編寫泛型程式碼時,必須時刻提醒自己, * 你只是看起來好像擁有關於引數的型別資訊而已。提醒自己:T 不,它值時一個Object * 擦除使用泛型並不是強制的 * @author zghw * */ class Frob{} class Fnorkle{} class Quark<Q>{} class Particle<POSITION,MOMENTUM>{} public class ErasedTypeEquivalence { /** * ArrayList<String>和ArrayList<Integer>很容易認為是不同的型別, * 不同的型別在行為方面肯定不同 但輸出發現c1==c2為true * @param args */ public static void main(String[] args) { Class c1=new ArrayList<String>().getClass(); Class c2=new ArrayList<Integer>().getClass(); System.out.println(c1==c2);//輸出為true List<Frob> list = new ArrayList<Frob>(); Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>(); Quark<Fnorkle> quark = new Quark<Fnorkle>(); Particle<Long,Double> part=new Particle<Long,Double>(); //Class.getTypeParameters()將返回一個Typeariable物件陣列,表示有泛型宣告 //的型別引數... 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(part.getClass().getTypeParameters())); /** * 輸出結果: * [E] [K, V] [Q] [POSITION, MOMENTUM] */ /** * 輸出結果是一些佔位符 * 說明:在泛型程式碼內部,無法獲得任何有關泛型引數型別的資訊。 * 當你在使用泛型時,任何具體的型別資訊都被擦除了,你唯一知道的就是你在使用一個物件。 * 因此,List<String>和List<Integer>在執行時事實上是相同的型別。這兩種形式都被 * 擦除成它們的原生型別,即List */ } }
package com.zghw.base.generic; /** * 擦除帶來的問題 * 1.無法建立型別例項 * 2.無法使用instanceof * 3.無法呼叫型別例項方法 * 解決辦法 * 通過引入型別標籤來對擦除進行補償。這意味著你需要顯示地傳遞你地型別地Class物件, * 以便你可以在型別表示式中使用它。 * 可以使用?extends T 繼承來實現可以呼叫方法 * @author zghw */ class Erased<T>{ public static void f(Object arg){ //if(arg instanceof T){}//Error無法使用instanceof //T var = new T();//Error無法建立型別例項 } } class Building {} class House extends Building{} public class ClassTypeCapture<T> { Class<T> kind; T t ; public ClassTypeCapture(Class<T> kind){ this.kind = kind; try { t=kind.newInstance();//建立型別例項 } catch (Exception e) { throw new RuntimeException(); } } public boolean isInstance(Object arg){ return kind.isInstance(arg); } public static void main(String[] args) { ClassTypeCapture<Building> c1=new ClassTypeCapture<Building>(Building.class); ClassTypeCapture<House> c2=new ClassTypeCapture<House>(House.class); System.out.println(c1.isInstance(c2)); System.out.println(c1.isInstance(new Building())); System.out.println(c1.isInstance(new House())); System.out.println(c2.isInstance(new Building())); System.out.println(c2.isInstance(new House())); } }
package com.zghw.base.generic;
/**
* 使用簡單工廠來建立泛型T 建議建立泛型物件使用此方法
*
* @author zghw
*
*/
interface Factory<T> {
T creat();
}
class IntegerGen implements Factory<Integer> {
@Override
public Integer creat() {
return new Integer(0);// 由於Integer沒有預設構造方法,無法使用Class建立泛型例項
}
}
class Widget {
public static class FactoryInner implements Factory<Widget> {
@Override
public Widget creat() {
return new Widget();
}
}
}
class Foo<T> {
private T x;
public <F extends Factory<T>> Foo(F factory) {
x = factory.creat();
}
public T get() {
return x;
}
}
public class FactoryConstraint {
public static void main(String[] args) {
Integer i = new Foo<Integer>(new IntegerGen()).get();
Widget w = new Foo<Widget>(new Widget.FactoryInner()).get();
}
}
package com.zghw.base.generic;
/**
* 使用模板方法建立泛型例項
*
* @author zghw
*
*/
abstract class GenericWithCreator<T> {
final T element;
public GenericWithCreator() {
element = creat();
}
abstract T creat();
}
class Obj {
}
class Creator extends GenericWithCreator<Obj> {
@Override
Obj creat() {
return new Obj();
}
void f() {
System.out.println(element.getClass().getSimpleName());
}
}
public class CreatorGeneric {
public static void main(String[] args) {
new Creator().f();
}
}
package com.zghw.base.generic;
import java.awt.Color;
/**
* 邊界 邊界作用: 可以用於在泛型地引數上設定限制條件。
* 你可以按照自己的邊界型別來呼叫方法。
* 因為擦除移除了型別,所以你可以用泛型引數呼叫的方法只是Object呼叫的方法。
* 但是,如果能夠將這個引數限制在某個型別子類,那麼你就可以用這些型別子集來呼叫方法。
* 為了執行這種限制,Java泛型重用了extends關鍵字
*
* @author zghw
*
*/
interface HasColor {
Color getColor();
}
class Dimension {
public int x, y, z;
}
interface Weight {
int weight();
}
class HoldItem<T> {
T item;
HoldItem(T item) {
this.item = item;
}
T getItem() {
return item;
}
}
class Colored<T extends HasColor> {
T item;
Colored(T item) {
this.item = item;
}
Color getColor() {
return item.getColor();
}
}
class Colored2<T extends HasColor> extends HoldItem<T> {
Colored2(T item) {
super(item);
}
Color getColor() {
return item.getColor();
}
}
class ColoredDimension<T extends Dimension & HasColor> {
T item;
ColoredDimension(T item) {
this.item = item;
}
Color getColor() {
return item.getColor();
}
int getX() {
return item.x;
}
int getY() {
return item.y;
}
int getZ() {
return item.z;
}
}
class ColoredDimension2<T extends Dimension & HasColor> extends Colored2<T> {
ColoredDimension2(T item) {
super(item);
}
int getX() {
return item.x;
}
int getY() {
return item.y;
}
int getZ() {
return item.z;
}
}
class Solid2<T extends Dimension & HasColor & Weight> extends
ColoredDimension2<T> {
Solid2(T item) {
super(item);
}
int weight() {
return item.weight();
}
}
class Solid<T extends Dimension & HasColor & Weight> {
T item;
Solid(T item) {
this.item = item;
}
Color getColor() {
return item.getColor();
}
int getX() {
return item.x;
}
int getY() {
return item.y;
}
int getZ() {
return item.z;
}
int weight() {
return item.weight();
}
}
class Bounded extends Dimension implements HasColor, Weight {
@Override
public int weight() {
return 0;
}
@Override
public Color getColor() {
return null;
}
}
public class BasicBounds {
public static void main(String args[]) {
Solid<Bounded> s = new Solid<Bounded>(new Bounded());
Solid2<Bounded> s2 = new Solid2<Bounded>(new Bounded());
s.getColor();
s.getX();
s2.getColor();
s2.getY();
}
}
package com.zghw.base.generic;
/**
* 只有當你希望使用的型別引數比某個具體型別(以及它的所有子型別)更加“泛化”時--也就是說,當你希望
* 程式碼能夠跨多個類工作時,時用泛型才有所幫助,通常比簡單的類替換要更復雜
* 必須檢視所有的程式碼,並確定它是否“足夠複雜”到必須時用泛型的程度
* @author zghw
*
*/
class HasF{
void f(){
System.out.println();
}
}
class Manipulator<T>{
private T obj;
public Manipulator(T obj){
this.obj =obj;
}
public void set(T obj){
this.obj = obj;
}
public T get(){
return obj;
}
}
//沒有泛型的類 比較上面的泛型類 可以發現泛型並沒有任何好處
//T擦除了HasF,就好像在類的宣告中用HasF一樣
class Manipulator1{
private HasF obj;
public Manipulator1(HasF obj){
this.obj =obj;
}
public void set(HasF obj){
this.obj = obj;
}
public HasF get(){
return obj;
}
}
public class Manipulation {
public static void main(String[] args) {
Manipulator<HasF> m = new Manipulator<HasF>(new HasF());
Manipulator1 m1 = new Manipulator1(new HasF());
//T擦除了HasF,就好像在類的宣告中用HasF一樣
}
}
package com.zghw.base.generic;
import java.util.*;
/**
* 萬用字元
* 為什麼需要萬用字元?
* 泛型沒有內建的協變型別
* 萬用字元
* 1.? extends T 用作方法返回值 意味著 它可以是任何事物包括子類,而編譯器無法驗證“任何事物”的安全性
* 2.? super T 用作方法引數 將一個T型別的物件或者從T匯出的任何物件作為引數傳入使用型別的方法
* 3.?
* 因此你可能會根據如何能夠向一個泛型型別”寫入“(傳遞給一個方法),
* 以及如何能夠從一個泛型型別中“讀取”(從一個方法中返回),來左手思考子型別和超型別邊界。
* @author zghw
*
*/
class Fruit{}
class Apple extends Fruit{}
class Jonathan extends Apple{}
class Orange extends Fruit{}
public class Covariance{
public static void main(String[] args) {
List<Fruit> list1 =new ArrayList<Fruit>();
List<Apple> list2 =new ArrayList<Apple>();
//編譯不通過 Apple的List在型別上不等價於Fruit的List,即使Apple是Fruit的子類
//我們在討論的是容器的型別,而不是容器持有的型別
//但是,我們想要在兩個型別之間建立某種型別的向上轉型關係,
//就需要萬用字元
//List<Fruit> list3 =new ArrayList<Apple>();//編譯不通過
//List<? extends Fruit> :你可以讀作”具有任何從Fruit繼承的型別的列表“
//但List不會持有任何型別的Fruit. list3引用沒有指定具體的型別
//只是為了向上轉型
List<? extends Fruit> list3 =new ArrayList<Apple>();
//只接受 add(? extends Fruit e) 不瞭解到底需要Fruit的那個具有子型別
//因此它不會接受任何Fruit,甚至只接受null
//? extends Fruit 意味著 它可以是任何事物,而編譯器無法驗證“任何事物”的安全性
//list3.add(new Apple());//編譯不通過
//list3.add(new Fruit());//編譯不通過
//list3.add(new Object());//編譯不通過
list3.add(null);
writeTo(new ArrayList<Fruit>(),new Apple());
}
/**
* ? super T 只作用泛型引數中,List將持有從T匯出的某種具體型別,這樣就可以安全的將一個T型別的物件
*或者從T匯出的任何物件作為引數給List方法
* @param fruit
* @param item
*/
static <T> void writeTo(List<? super T> fruit,T item){
fruit.add(item);
}
}
package com.zghw.base.generic;
import java.util.*;
/**
* 萬用字元 ? extends T ? super T 的應用
*
* @author zghw
*
*/
public class GenericWriterReader {
static <T> void writeExact(List<T> list, T item) {
list.add(item);
}
static <T> void writeWild(List<? super T> list, T item) {
list.add(item);
}
static <T> T readExact(List<T> list) {
return list.get(0);
}
static class Reader<T> {
static <T> T readExact(List<T> list) {
return list.get(0);
}
}
static class ReadWild<T> {
static <T> T readExact(List<? extends T> list) {
return list.get(0);
}
}
static List<Fruit> fruitsw = new ArrayList<Fruit>();
static List<Apple> applesw = new ArrayList<Apple>();
static List<Fruit> fruits = Arrays.asList(new Fruit());
static List<Apple> apples = Arrays.asList(new Apple());
static void w1() {
writeExact(applesw, new Apple());
writeExact(fruitsw, new Apple());
}
static void w2() {
writeWild(applesw, new Apple());
writeWild(fruitsw, new Apple());
}
static void r1() {
Apple a = readExact(apples);
Fruit f = readExact(fruits);
f = readExact(apples);
}
static void r2() {
Reader<Fruit> r = new Reader<Fruit>();
Apple a = r.readExact(apples);
Fruit f = r.readExact(fruits);
f = r.readExact(apples);
}
static void r3() {
ReadWild<Fruit> r = new ReadWild<Fruit>();
Apple a = r.readExact(apples);
Fruit f = r.readExact(fruits);
f = r.readExact(apples);
}
public static void main(String[] args) {
w1();
w2();
r1();
r2();
r3();
}
}