所谓泛型,就是变量类型的参数化。
泛型是java1.5中引入的一个重要特征,通过引入泛型,可以使编译时类型安全,运行时更少抛出ClassCastException的可能。
方法、类或者接口中变量的类型定义成参数形式,然后在调用或者实例化时传入具体类型。
使用泛型时如果不提供参数类型,即泛型类没有参数化,系统只会警告,此时类型为Object。
/**基础使用*/
List<String> list = new ArrayList<String>();
// list.add(1);
list.add("aaa");
//JDK1.7 版本可以省略后面的String
List<String> jdkList = new ArrayList<>();
jdkList.add("aaa");
// jdkList.add(1);
注意点:
- <>中放的必须是引用数据类型, 不能是基本数据类型如int, double;
- 前后的泛型必须一致. 如上面所示,而不能是
List<String> list = new ArrayList<Integer>()
。
因此在JDK1.7时就推出了一个新特性叫菱形泛型(The Diamond), 就是说后面的泛型可以省略直接写成<> - 不能创建泛型数组
- 无法从静态上下文中引用非静态 类型变量 T,即静态方法无法引用类的泛型,只能用方法泛型
使用泛型的好处
- 提高安全性: 将运行期的错误转换到编译期. 如果我们在对一个对象所赋的值不符合其泛型的规定, 就会编译报错。
- 省去强转的麻烦: 比如我们在使用List时, 如果我们不使用泛型
泛型中的标记符
E - Element (在集合中使用,因为集合中存放的是元素)
**T **- Type(Java 类)
**K **- Key(键)
**V **- Value(值)
**N **- Number(数值类型)
?- 表示不确定的java类型
**S、U、V ** - 2nd、3rd、4th types
Object跟这些标记符代表的java类型有啥区别呢?
Object是所有类的根类,任何类的对象都可以设置给该Object引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型T、E等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换。且泛型使用上限时可以使用父类的方法,这点Object做不到。
@Test
public void testFan(){
/**基础使用*/
List<String> list = new ArrayList<String>();
// list.add(1);
list.add("aaa");
//JDK1.7 版本可以省略后面的<String>
List<String> jdkList = new ArrayList<>();
jdkList.add("aaa");
// jdkList.add(1);
/**泛型类*/
String spring = "spring";
TestFan<String> testFan = new TestFan(spring);
Integer i_testE = 2;
/**泛型方法和泛型类没有任何关系*/
testFan.testU(i_testE);
testFan.testT(i_testE);
testFan.testPrintCurrentT();
testFan.testStaticT(i_testE);
/**泛型上限
* 可以多边界
* <T extends A & B & C>
* 但是多边界使用时其实只能继承一个父类,
* 并且要将他写在第一个位置上. 其他的都是其实现的接口, 如图十九:
* 不在第一个位置上就会编译出错。
* */
TestFan<Integer> testFanInteger = new TestFan(1);
TestFan<Float> testFanFloat = new TestFan(0.1f);
TestFan<Double> testFanDouble = new TestFan(0.24d);
TestFan<String> testFanString = new TestFan("nihao");
Number number= new Number() {
@Override
public int intValue() {
return 10;
}
@Override
public long longValue() {
return 10;
}
@Override
public float floatValue() {
return 10;
}
@Override
public double doubleValue() {
return 10;
}
@NonNull
@Override
public String toString() {
return "number + 10";
}
};
TestFan<Number> testFanNumber = new TestFan(number);
testFan.testUpT(testFanInteger);
testFan.testUpT(testFanFloat);
testFan.testUpT(testFanDouble);
testFan.testUpT(testFanNumber);
//错误: 不兼容的类型: TestFan<String>无法转换为TestFan<? extends Number>
// testFan.testUpT(testFanString);
/**泛型下限*/
TestFan<Integer> testFanDownInteger = new TestFan(1);
TestFan<Float> testFanDownFloat = new TestFan(0.1f);
TestFan<Number> testFanDownNumber = new TestFan(number);
testFan.testDownT(testFanDownInteger);
//错误: 不兼容的类型: TestFan<Float>无法转换为TestFan<? super Integer>
// testFan.testDownT(testFanDownFloat);
testFan.testDownT(testFanDownNumber);
/**
* 泛型接口
*/
//1.实现时已处理
InterfaceImplTow interfaceImplTow = new InterfaceImplTow();
interfaceImplTow.testCall("wo yi shi xian FanXing");
//2.实例化处理
InterfaceImplOne<String> interfaceImplOne = new InterfaceImplOne();
interfaceImplOne.testCall("shi li hua shi xian");
/**泛型继承
* 如果传入的变量类型是泛型的子类,可以传入
* */
TestFan<Number> testFanExtends = new TestFan<>(1);
Integer integer = 1;
Double aDouble = 2.1d;
Float aFloat = 3.1f;
testFanExtends.testClassFan(integer);
testFanExtends.testClassFan(aDouble);
testFanExtends.testClassFan(aFloat);
//错误: 不兼容的类型: String无法转换为Number
// testFanExtends.setId("String");
/**数组不能有泛型*/
//错误: 创建泛型数组
/**Person 和 PersonObject区别
* 展示了用泛型比用Object的好处,
* 可以使用父类的方法*/
}
public class TestFan<T> {
private T t;
public TestFan(T t) {
this.t = t;
}
//这里的T来自类的T
public String testClassFan(T t) {
this.t = t;
return t.toString();
}
public <U> U testU(U e){
System.out.println(e.toString());
return e;
}
//这里是泛型方法的T与类中的T无任何关系
public<T> T testT(T t1){
System.out.println(t1.toString());
return t1;
}
public void testPrintCurrentT(){
System.out.println(t.toString());
}
//错误: 无法从静态上下文中引用非静态 类型变量 T
// public static void testStaticT(T tStatic){
// System.out.println(tStatic.toString());
// }
public static <F> void testStaticT(F tStatic){
System.out.println(tStatic.toString());
}
public void testUpT(TestFan<? extends Number> testFan){
testFan.testPrintCurrentT();
}
public void testDownT(TestFan<? super Integer> testFan){
testFan.testPrintCurrentT();
}
}
public interface TestFanInterface<T> {
void testCall(T t);
}
public class InterfaceImplOne<T> implements TestFanInterface<T> {
@Override
public void testCall(T t) {
System.out.println(t.toString());
}
}
public class InterfaceImplTow implements TestFanInterface<String> {
@Override
public void testCall(String s) {
System.out.println(s);
}
}
// public class Person<T extends String> {
// private T name;
//
// public Person(T id) {
// this.name = id;
// }
//
// public T getId() {
// return name;
// }
//
// public void setId(T id) {
// this.name = id;
// }
//
// public boolean isContainsString(String a){
// return name.contains(a);
// }
// }
//
// public class PersonObject {
// private Object name;
// public PersonObject(Object id) {
// this.name = id;
// }
//
// public Object getId() {
// return name;
// }
//
// public void setId(Object id) {
// this.name = id;
// }
//
// public boolean isContainsString(String a){
// return name.contains(a);
// }
// }
?和 上下边界问题
通配符不能出现在类声明上,编译器会报错。
单独的? 即表示 ?extends object。
参考下面的代码会发现:
<? extends Person> 上边界通配符,只能get不能Add,IN。
<? super Person>下边界通配符,只能add不能get,OUT。
IN 表示只读,不能存数据;OUT表示存数据,不能读。官网进行了in 和 out说明
上下边界的出现是为了解决泛型无法逆变与协变的问题(关于逆变与协变问参考文章)。
Number num = new Integer(1);
ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch
编译器报错
上下边界使用场景:方法参数类型,因为通常使用变量使用我需要既能读也能存
List<? super Student> listDown = new ArrayList<>();
List<? extends Person> listUp;
/**
* 下界OUT
*/
// listDown.add(new Person("001"));
listDown.add(new Student("002"));
//编译器报错类型不一致
// Person personDown = listDown.get(0);
//------------分界线-------------
/**
* 上界IN
*/
ArrayList arrayList = new ArrayList();
arrayList.add(new Person("003"));
listUp = arrayList;
// listUp.add(new Person("001"));
// listUp.add(new Student("002"));
Person personUp = listUp.get(0);
System.out.println(personUp.getName());
网友评论