uni-app開發微信小程式自定義tabbar欄和根據不同角色展示不同的tabbar欄
目錄公眾號:菜雞幹Java
上次講了集合,就是放物件的容器,但是集合並不知道物件的具體資料型別,所以很容易發生異常。比如:
List a = new ArrayList();
a.add("as");
a.add("end");
a.add(2);
a.forEach(a->System.out.println(((String)a).length()) );//強制轉換錯誤
Java 泛型generics
是JDK 5中引入的一個新特性, 泛型提供了編譯時型別安全檢測機制,該機制允許程式設計師在編譯時檢測到非法的型別。泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。
假定我們有這樣一個需求:寫一個排序方法,能夠對整型陣列、字串陣列甚至其他任何型別的陣列進行排序,該如何實現?使用 Java 泛型的概念,我們可以寫一個泛型方法來對一個物件陣列排序。然後,呼叫該泛型方法來對整型陣列、浮點數陣列、字串陣列等進行排序。這樣不僅大大減少了程式碼量,還利於避免不必要的異常!
泛型菱形
語法
從Java7開始,在用構造器建立物件時後面不用再帶上泛型了,簡化了程式碼,例如:
List<String> a = new ArrayList<>();
定義介面、類和泛型方法
Java5為介面和類增加了泛型支援,從而可以在建立集合物件時傳入型別實參。
public interface List<E>{ void add(E x); Iterator<E> iter(); } public interface StrList extends List{ void add(String x); Iterator<String> iter(); } public interface StrList extends List<String>{ void add(String x); Iterator<String> iter(); } public interface Map<K,V>{ ... } public class human<T>{//定義一個泛型類 public T man; public human(){} public human(T){} public T getInfo(){ return this.man; } } public class A extends human<String>{ public String getInfo(){ return ":"+super.getInfo(); } }
上述程式碼中,定義一個泛型介面,可以衍生出多個型別子介面,同樣一個帶泛型的父類可以衍生出許多子類,當使用這些介面和父類時,不能再包含型別形參!但是可以不傳入型別實參,例如:
public class A extends human{
public String getInfo(){
return super.getInfo().toString();
}//返回字串型別
}
如果需要重寫繼承父類的子類方法,則需要注意型別。對於泛型,只是允許程式設計師在編譯時檢測到非法的型別而已。但是在執行期時,其中的泛型標誌會變化為Object
型別。
泛型方法
方法想定義自己的泛型形參也是允許的,這樣還提供了對泛型方法的支援。<>括號內為型別形參
格式為:修飾符 <T,V> 返回值型別 方法名(形參列表){...}
public class Test{
static <T> void test(Collection<? extends T> ele,Collection<T> a){
for(T ele: a){
a.add(ele);
}
}
public static void main(String[] args){
List<Object> ao = new ArrayList<>();
List<String> as = new ArrayList<>();
test(as,ao);
}
}
泛型構造器
class Output{
public <T> Output(T t){
System.out.println(t);
}
}
public class Test{
public static void main(String[] args){
new Output("發");
new Output(666);
new <String> Output(3.14);//實參是Double型別出錯
}
}
泛型構造器可以認為是泛型方法的特殊一種,不過大致上它們都差不多!
型別萬用字元?
使用泛型類時,都應該為這個泛型類傳入實參,否則會提出警告。型別萬用字元一般是使用?
代替具體的型別引數,它可以匹配任何型別,List<?>在邏輯上是List
public void test(List<?> c){
System.out.println(c.get(0));
}
這種型別萬用字元的List
僅表示它是各種泛型List的父類,並不能直接新增元素。例如:
List<?> c = new ArrayList<String>();
c.add(new Object());
由於無法得知c中的元素型別,所以不能向其中新增物件,除了新增null
。
型別萬用字元上限
public void getNumber(List<? extends Number> data) {
System.out.println("data :" + data.get(0));
}
<? extends T>
表示該萬用字元所代表的型別是T型別的子類(或本身),<? super T>
表示該萬用字元所代表的型別是T型別的父類。
型別萬用字元下限
型別萬用字元下限通過形如List<? super Number>
來定義,表示型別只能接受Number
本身及其父類型別,如Object
型別的例項。
泛型方法與型別萬用字元的區別
什麼時候用型別萬用字元,什麼時候用泛型方法呢?
- 萬用字元能用來支援靈活的子類化
- 泛型方法允許型別形參用來表示一個或多個引數或引數與返回值之間的依賴關係
- 型別萬用字元可以在方法名中定義形參型別,也可以定義變數型別;但泛型方法的型別形參必須在顯示宣告
設定型別形參的上限
泛型不僅允許使用萬用字元形參上限,還可以設定型別形參的上限。例如:
public class Apple<T extends Number>{//Number及其子類
public static void main(String[] args){
Apple<Integer> a = new Apple<>();
Apple<String> b = new Apple<>();//String不是Number的子類,將引起異常
}
}
上面定義了一個泛型類
,形參上限是Number
及其子類,傳入一個String
類將導致編譯錯誤。如果需要設定多個上限和介面,表示該形參型別既是父類及其子類,並且實現了多個上限介面。需要注意的是所有介面上限
必須在類上限之後,類上限
始終在前面。
public class Apple<T extends Number & java.io.Serializable>{}
擦除和轉換
嚴格泛型程式碼裡,帶泛型宣告的類總應該帶著型別引數。但是允許使用帶泛型宣告的類時,不指定型別實參,如果沒有指定的話,則該型別引數被稱作raw type
原始型別,預設宣告為該型別引數的第一個上限型別!
當把一個帶泛型資訊的物件賦給另一個沒有泛型資訊的變數時,泛型資訊將拋棄,稱擦除
。比如一個List<String>
型別轉給List
型別,該List
的型別上限變為Object
。 如果將List
物件賦給一個List<String>
物件,不會引起編譯錯誤,但是提示未經檢查的轉換
。
List<Integer> a = new ArrayList<>();
a.add(6);
List list = a;
List<String> ls = list;//引起警告
System.out.println(ls.get(0));//轉換將引起錯誤
泛型與陣列
陣列元素型別不能包含型別變數或形參,除非無上限型別萬用字元,但可以宣告元素型別包含型別變數或形參的陣列。比如:只能說明List<String>[]
形式的陣列,不能建立ArrayList<String>[10]
這樣的陣列。
List<String>[] a = new ArrayList[10]; //沒有問題