問題:在獲取用戶信息的API中,後臺給咱們返回一個這樣形式的json字符串。java
{
"meta": {
"code": 0,
"message": "ok"
},
"data": {
"nick_name": "hellokitty",
"cellphone": "18301824843",
}
}
複製代碼
咱們用fastJson解析上述json字符串時候,該怎麼處理? ,咱們是否是就會寫這樣一個類。json
public class User {
private Meta meta;
private Data data;
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
static class Meta
{
private String code;
private String message;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
static class Data
{
private String nick_name;
private String cellphone;
public String getNick_name() {
return nick_name;
}
public void setNick_name(String nick_name) {
this.nick_name = nick_name;
}
public String getCellphone() {
return cellphone;
}
public void setCellphone(String cellphone) {
this.cellphone = cellphone;
}
}
}
複製代碼
而後調用fastjason的JSON.parseObject(msg,User.class)
進行解析。安全
而若是拉取設備列表API返回的數據格式是這樣的一個形式,咱們該怎麼處理?bash
{
"meta": {
"code": 0,
"message": "ok"
},
"data": [
{
"device_id": "4acb634aaf5711e8b290000c29c27f42",
"role": 1,
"device_alias": "hellokitty",
"created_at": "2018-09-04T10:55:57"
},
{
"device_id": "4acb634aaf5711e8b290000c29c27f42",
"role": 1,
"device_alias": "hellokitty",
"created_at": "2018-09-04T10:55:57"
}
]
}
複製代碼
是否是咱們仍然要再寫一個解析類來解析這個設備列表類。ide
public class DeviceList {
private Meta meta;
private List<Device> data;
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public List<Device> getData() {
return data;
}
public void setData(List<Device> data) {
this.data = data;
}
static class Meta
{
private String code;
private String message;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
static class Device
{
@Override
public String toString() {
return "Device{" +
"device_id='" + device_id + '\'' + ", role=" + role + ", device_alias='" + device_alias + '\'' + ", created_at='" + created_at + '\'' +
'}';
}
private String device_id;
private int role;
private String device_alias;
private String created_at;
public String getDevice_id() {
return device_id;
}
public void setDevice_id(String device_id) {
this.device_id = device_id;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
public String getDevice_alias() {
return device_alias;
}
public void setDevice_alias(String device_alias) {
this.device_alias = device_alias;
}
public String getCreated_at() {
return created_at;
}
public void setCreated_at(String created_at) {
this.created_at = created_at;
}
}
}
複製代碼
若是每次都這樣的話,會不會要建立不少很相像的類,他們只是裏面部分變量不一樣,其餘的部分都相同。ui
再舉一個栗子: 若是咱們想要產生多個對象,每一個對象的邏輯徹底同樣,只是對象內的成員變量的類型不一樣,那咱們如何去作? 在下面咱們建立了兩個類,只是data的變量類型不一樣,是否是也能夠達到咱們剛纔的要求。this
static class MyClass1
{
public MyClass1() {
}
private String data;
public MyClass1(String data) {
this.data = data;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
static class MyClass2
{
public MyClass2() {
}
private int data;
public MyClass2(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
複製代碼
打印結果:spa
MyClass1 myClass1 = new MyClass1();
myClass1.setData("Cyy");
MyClass2 myClass2 = new MyClass2();
myClass2.setData(10);
System.out.println(myClass1.getData());
System.out.println(myClass2.getData());
複製代碼
輸出:code
Cyy
10
複製代碼
可是若是咱們還想要這樣一個對象呢,那咱們是否是還要繼續去建立這樣的對象,那若是我還要10個這個的對象呢,那咱們是否是就要建立十個。這樣明顯是很笨重的一種解決方案。對象
那咱們如今思考,若是咱們用Object來代替呢?
static class MyClass1
{
public MyClass1() {
}
private Object data;
public MyClass1(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
複製代碼
打印輸出:
MyClass1 myClass1 = new MyClass1();
myClass1.setData("Cyy");
System.out.println((String)myClass1.getData());
MyClass1 myClass2 = new MyClass1();
myClass2.setData(10);
System.out.println((int)myClass2.getData());
```
輸出結果:
複製代碼
Cyy
10
複製代碼
呀~看上去好像完美解決了,不用建立多個類,就能夠實現剛纔須要功能,好像很完美,如今讓他變成不完美,如今咱們讓他這樣打印出來.
複製代碼
MyClass1 myClass2 = new MyClass1();
myClass2.setData(10);
System.out.println((String)myClass2.getData());
複製代碼
注意咱們給他的是整型,可是打印時候咱們給他的強轉類型是String,如今看下會發生什麼問題。
複製代碼
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at SecDemo.main(SecDemo.java:13)
複製代碼
它提示了,類型轉換異常。
總結
方案(一) :
方法: 建立多個類文件,給每一個類中的成員變量設置指定的數據類型。
缺點: 致使類的膨脹,重用性太差
方案(二) :
方法: 建立一個類文件,給這個類中的成員變量設置Object數據類型
缺點:編譯的時候正常,但運行時候可能會報錯.
泛型類就能很好的解決以上兩個問題。
複製代碼
泛型是JDK1.5引入的新特性,也是最重要的一個特性。
泛型能夠在編譯的時候檢查類型安全
,而且全部的強制轉換都是自動和隱式的。
泛型的原理就是類型的參數化
,即把類型看作參數,也就是說把所要操做的數據類型看作參數,就像方法的形式參數是運行時傳遞的值同樣。
簡單的說,類型變量扮演的角色如同一個參數,它提供給編譯器用來類型檢查的信息。
泛型能夠提升代碼的擴展性和重用性 **
若是咱們將剛纔的類改爲泛型類是什麼樣子的呢?
static class MyClass1<T>
{
public MyClass1() {
}
private T data;
public MyClass1(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
複製代碼
咱們發如今類的開頭多了個,這個就表明着傳入進來的參數,他能夠是整型,能夠是字符串類型,只要你傳進來了那麼後續的get,set方法就所有都是這種類型了。他就至關於一個操做的參數。好的如今咱們試一下。
打印輸出:
MyClass1 myClass1 = new MyClass1<String>();
myClass1.setData("Cyy");
System.out.println(myClass1.getData());
MyClass1 myClass2 = new MyClass1<Integer>();
myClass2.setData(10);
System.out.println(myClass2.getData());
複製代碼
輸出:
Cyy
10
複製代碼
有沒有發現,咱們不用進行強制類型轉換仍然能輸出正確的數值。 注意下,當咱們new MyClass1<String>()
傳的是String
那麼咱們類裏面的全部T
就都是String
類型了。
總結:
泛型類使用優勢:
防止類膨脹
再也不手動進行類型轉換
static class MyClass1<T1>
{
public MyClass1() {
}
private T1 data1;
public T1 getData1() {
return data1;
}
public void setData1(T1 data1) {
this.data1 = data1;
}
}
static class Student
{
private String name;
public Student(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 複製代碼
使用:
MyClass1<MyClass1<Student>> myClass1MyClass1 = new MyClass1<MyClass1<Student>>();
MyClass1<Student> myClass1 = new MyClass1<Student>();
myClass1.setData1(new Student("cyy"));
myClass1MyClass1.setData1(myClass1);
System.out.println(myClass1MyClass1.getData1().getData1().toString());
複製代碼
輸出:
Student{name='cyy'}
複製代碼
static class MyClass1<T1,T2>
{
public MyClass1() {
}
private T1 data1;
private T2 data2;
public T2 getData2() {
return data2;
}
public void setData2(T2 data2) {
this.data2 = data2;
}
public T1 getData1() {
return data1;
}
public void setData1(T1 data1) {
this.data1 = data1;
}
}
複製代碼
使用:
MyClass1<String,Integer> myClass1 = new MyClass1<String,Integer>();
myClass1.setData1("Cyy");
myClass1.setData2(25);
System.out.println(myClass1.getData1());
System.out.println(myClass1.getData2());
複製代碼
輸出:
Cyy
25
複製代碼
class SuperClass<T1>
{
private T1 var1;
public SuperClass(T1 var1) {
this.var1 = var1;
}
public T1 show1()
{
return var1;
}
}
class SubClass<T1,T2> extends SuperClass<T1>
{
private T2 var2;
public SubClass(T1 var1, T2 var2) {
super(var1);
this.var2 = var2;
}
@Override
public T1 show1() {
return super.show1();
}
}
複製代碼
使用:
SubClass<String,Integer> subClass = new SubClass<>("cyy",25);
System.out.println(subClass.show1());
複製代碼
輸出:
cyy
複製代碼
interface IInfo<T2>
{
public void show2(T2 var3);
}
static class SubClass<T1,T2> extends SuperClass<T1> implements IInfo<T2>
{
private T2 var2;
public SubClass(T1 var1, T2 var2) {
super(var1);
this.var2 = var2;
}
@Override
public T1 show1() {
return super.show1();
}
@Override
public void show2(T2 var3) {
System.out.println(var3);
System.out.println(var2);
}
}
複製代碼
使用:
SubClass<String,Integer> subClass = new SubClass<>("cyy",25);
subClass.show2(100);
System.out.println(subClass.show1());
複製代碼
輸出:
100
25
cyy
複製代碼
注:不能夠進行泛型變量之間的運算,由於泛型變量在編譯期間會進行類型擦除,所有變成Object,好比Object+Object就不知道是什麼類型了,因此這點很重要。
OK,如今咱們能夠回到最初那個問題上了,咱們能夠利用泛型定義一個CommResult類。
public class CommResult <T> {
private Meta meta;
private T data;
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
static class Meta
{
private String code;
private String message;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
}
複製代碼
而後使用的時候咱們能夠這樣:
JSON.parseObject(msg,CommResult<User>)
或JSON.parseObject(msg,CommResult<List<Device>>)
。
這樣就完美避免了建立多個結構同樣,可是隻有裏面部分變量不一致的類了。
在定義泛型類別時,默認在實例化泛型類的時候可使用任何類型,可是若是想要限制使用泛型時,只能用某個特定類型或者是其子類型才能實例化該類型時,能夠在定義類型時,使用extends
關鍵字指定這個類型必須是繼承某個類,或者實現某個接口。 當沒有指定泛型繼承的類型或接口時,默認使用extends Object,因此默認狀況下,可使用任何類型做爲參數。
class GenericClass<T extends Animal>
{
private T data;
public GenericClass(T data)
{
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
abstract class Animal
{
public abstract void eat();
}
class Dog extends Animal
{
@Override
public void eat() {
System.out.println("啃骨頭");
}
}
class Cat extends Animal
{
@Override
public void eat() {
System.out.println("吃魚肉");
}
}
複製代碼
如今咱們看下,若是我在泛型類裏面傳個String
類型的參數,看他會報什麼? Type parameter 'java.lang.String' is not within its bound; should extend 'Test.Animal
他說String不是Animal子類,不行吧。
若是咱們換成這樣就能夠了。
GenericClass<Dog> genericClass = new GenericClass<>(new Dog());
genericClass.getData().eat();
GenericClass<Cat> genericClasscat = new GenericClass<>(new Cat());
genericClasscat.getData().eat();
複製代碼
若是換成接口呢?
class GenericClass<T implements eat>
{
private T data;
public GenericClass(T data)
{
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
複製代碼
這樣寫對不對,這樣寫是不對的,編譯器會報錯的,由於不論是接口仍是類,都要用extends
。因此換成接口也要寫成這樣就能夠了。
class Cat implements eat
{
@Override
public void eat() {
System.out.println("吃魚肉");
}
}
class Dog implements eat
{
@Override
public void eat() {
System.out.println("啃骨頭");
}
}
interface eat
{
public abstract void eat();
}
class GenericClass<T extends eat>
{
private T data;
public GenericClass(T data)
{
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
複製代碼
同一泛型類,若是實例化時給定的實際類型不一樣,則這些實例的類型是不兼容的,不能相互賦值。如:
Generic<Boolean> f1 = new Generic<Booleab>();
Generic<integer> f2 = new Generic<integer>();
f1 = f2;//發生編譯錯誤
Generic<Object> f = f1 ;//f1和f類型並不兼容,發生編譯錯誤
f = f2;//f2和f類型一樣不兼容,也會發生編譯錯誤。
複製代碼
泛型類實例之間的不兼容性會帶來使用的不便。咱們可使用泛型通配符(?)生命泛型類的變量就能夠解決這個問題。
泛型通配的使用方式
Generic<Boolean> f1 = new Generic<Booleab>();
Generic<?> f= f1;
複製代碼
Generic<Dog> f1 = new Generic<Dog>();
Generic<? extends Animal> f= f1;
複製代碼
Generic<Animal> f1 = new Generic<Animal>();
Generic<? super Dog> f= f1;
複製代碼
如今要在這裏特別說下兩個限定通配符
extends
上邊界限定通配符舉個例子一看就懂了,<? extends Animal> , 那這裏的`?`就必須是Animal的子類或它本身。
複製代碼
super
下邊界限定通配符舉個例子一看就懂了,<? super Dog> , 那這裏的`?`就必須是Dog的父類或它本身。
複製代碼
不只類能夠聲明泛型,類中的方法也能夠聲明僅用於自身的泛型,這種方法叫作泛型方法。其定義格式爲:
訪問修飾符<泛型列表> 返回類型 方法名(參數列表)
{
實現代碼
}
複製代碼
在泛型列表中聲明的泛型,可用於該方法的返回類型
聲明,參數類型
聲明和方法代碼中的局部變量
的類型聲明。
類中其餘方法不能使用當前方法聲明的泛型。
注:是否擁有泛型方法,與其所在的類是不是泛型沒有關係。要定義泛型方法,秩序將泛型參數列表置於返回值以前。
何時使用泛型方法,而不是泛型類呢?
添加類型約束只做用於一個方法的多個參數之間,而不涉及類中的其餘方法時。
施加類型約束的方法爲靜態方法,只能將其定義爲泛型方法,由於靜態方法不能使用其所在類的類型參數。
再舉個代碼的例子:
如今咱們先定義一個泛型類:
public class Demo1 {
public static void main(String[] args)
{
GenericClassOne<String> genericClassOne = new GenericClassOne<>();
genericClassOne.printlinT(10);
}
}
class GenericClassOne<T>
{
public void printlinT(T content)
{
System.out.println(content);
}
}
複製代碼
若是咱們這麼寫,確定編譯就報錯誤了吧,由於咱們上面定義的是String
類型,可是咱們傳給他的是int
型的。那若是這樣的話,這個方法是否是就有侷限性了。
那若是咱們如今使用泛型方法呢?該怎麼寫?
public class Demo1 {
public static void main(String[] args)
{
GenericClassOne genericClassOne = new GenericClassOne();
genericClassOne.printlinT(10);
genericClassOne.printlinT("cyy");
genericClassOne.printlinT(12.5);
}
}
class GenericClassOne<T>
{
//泛型方法,類型定義寫在返回值以前了
public <T> void printlinT(T content)
{
System.out.println(content);
}
}
複製代碼
這下不會再報編譯錯誤了,如今看下打印結果。
輸出:
10
cyy
12.5
複製代碼
這樣是否是就靈活了許多啦~
那麼泛型的方法可不能夠重載呀,固然能夠,咱們仍然能夠寫成這樣。
class GenericClassOne<T>
{
//泛型方法,類型定義寫在返回值以前了
public <T> void printlinT(T content)
{
System.out.println(content);
}
//泛型方法,類型定義寫在返回值以前了
public <T extends Animal> void printlinT(T animal)
{
animal.eat();
}
}
abstract class Animal
{
public abstract void eat();
}
複製代碼
由於泛型類在編譯過程當中會有個擦除的工做,因此第一個printlnT(T content)中的泛型會變成object,而第二個泛型方法中的T會變成Animal。因此他的方法能夠被重載。
Ok,結束!