Java object-oriented (XII)

Generics

What is a generic

Generics: a wide range of generic type

Beginning not sure what type, when in use, to determine what type

(1) At the beginning of the definition, leaving a jack

(2) when the object is created, again inserted into the corresponding type

Generics can be understood as "parameterized type." Mention of parameters, the most familiar with is visible when defining method parameters, and then call this method when passing arguments.

As the name suggests, is a specific type as a parameter of the original, similar to the process variable parameter types are also defined at this time in the form of parameters (type parameter may be referred to), and particularly when using the incoming / calling type (type arguments).

Generic for parameterized types (not created in case of a new type, to control the type parameter particularly limited by different types of generic specified). That is the generic use, the data type of the operation is specified as a parameter, which can be used in the type of classes, interfaces and methods, are called generic class, generic interfaces, generic method.

Generic definitions

Generic class: public class Demo {}, T represents an unknown type.

Generic interface: public interface ImplDemo <T, V> {}, and define the same class (the interface is a special class).

Generic method: public void demo1(T name){ } , public T demo2(T t){ return t;}

<> Brackets, the name may be any commonly used T: Type, E: Element, K: Key, V: Value.

Code template type of uncertainty, who call the sections of the code, and anyone can to indicate this type.

If you do not specify a generic type, which is the default type Object

Generic benefits

Determine (1) compile-time type, type of security guarantee, to avoid the type conversion exception.

(2) to avoid the cast.

(3) Code beneficial reuse, increased versatility.

Generic nature

Generics is actually a syntactic sugar, essence, or Object, but it is still to do strong internal transfer.

 
Copy
class Demo<T> { T x; public Demo(T x) { this.x = x; } public T getX() { return x; } } public class Test { public static void main(String[] args) { Demo<String> demo = new Demo<>("SSS"); String str = demo.x; System.out.println(str); } }

Decompile after:

 
Copy
class Demo { Object x; public Demo(Object x) { this.x = x; } public Object getX() { return x; } } public class Test { public Test() { } public static void main(String args[]) { Demo demo = new Demo("SSS"); String str = (String)demo.x; System.out.println(str); } }

Generic method and generic class

(1) generic class

Generic class defined above, class class name {}, In the creation of the object, to specify the generic type.

Generic generic class which is defined only by the above conventional method, the above can not be used in a static method.

(2) a generic method

In the above method is added generics, at the time of use, to determine the specific type of parameter passing.

The method you want to use generics alone, there must be meaningful parameters.

 
Copy
class Demo { public <T> void demo1(T t) { System.out.println(t.getClass()); } public <T> T demo2(T t) { System.out.println(t.getClass()); return t; } public static <T> void demo3(T t) { System.out.println(t.getClass()); } } public class Test { public static void main(String[] args) { Demo demo = new Demo(); demo.demo1("str");// class java.lang.String demo.demo1(10);// class java.lang.Integer demo.demo1(10.5);// class java.lang.Double Demo.demo3(true);// class java.lang.Boolean } }

Generic wildcards

Wildcard: I do not know what type to use when received, can be used? Indicating an unknown.

(1) wildcard borderless

The main role of unbounded wildcard is to allow generics to accept an unknown type of data.

 
Copy
public static void printList(List<?> list) { for (Object o : list) { System.out.println(o); } } public static void main(String[] args) { List<String> l1 = new ArrayList<>(); l1.add("aa"); l1.add("bb"); l1.add("cc"); printList(l1); List<Integer> l2 = new ArrayList<>(); l2.add(11); l2.add(22); l2.add(33); printList(l2); }

This use List <?> Way is the parent class reference point to a subclass object. Note that this method can not be written as the printList public static void printList (List list)的形式,虽然Object类是所有类的父类,但是List跟其他泛型的List如List , List 不存在继承关系,因此会报错。

我们不能对List<?>使用add方法,仅有一个例外,就是add(null)。 为什么呢?因为我们不确定该List的类型,不知道add什么类型的数据才对,只有null是所有引用数据类型都具有的元素

      
Copy
public static void addTest(List<?> list) { list.add(new Object()); // 编译报错 list.add(1); // 编译报错 list.add("ABC"); // 编译报错 list.add(null); } public static void main(String[] args) { List<?> list = new ArrayList<>(); addTest(list); }

由于我们根本不知道list会接受到具有什么样的泛型List,所以除了null之外什么也不能add。

还有, List<?>也不能使用get方法,只有Object类型是个例外。 原因也很简单,因为我们不知道传入的List是什么泛型的,所以无法接受得到的get,但是Object是所有数据类型的父类,所以只有接受他可以。

      
Copy
public static void getTest(List<?> list) { String s = list.get(0); // 编译报错 Integer i = list.get(1); // 编译报错 Object o = list.get(2); } public static void main(String[] args) { List<?> list = Arrays.asList(1, 2, 3, 4); getTest(list); }

(2)固定上边界的通配符

用来限定元素的类型必须得指定类的子类(包括指定类和指定类的子类)

      
Copy
public static double sumOfList(List<? extends Number> list) { double s = 0.0; for (Number n : list) { // 注意这里得到的n是其上边界类型的, 也就是Number, 需要将其转换为double. s += n.doubleValue(); } return s; } public static void main(String[] args) { List<Integer> list1 = Arrays.asList(1, 2, 3, 4); System.out.println(sumOfList(list1)); List<Double> list2 = Arrays.asList(1.1, 2.2, 3.3, 4.4); System.out.println(sumOfList(list2)); }

List<? extends E> 不能使用add方法

      
Copy
public static void addTest2(List<? extends Number> l) { // l.add(1); // 编译报错 // l.add(1.1); //编译报错 l.add(null); }

泛型<? extends E>指的是E及其子类,这里传入的可能是Integer,也可能是Double,我们在写这个方法时不能确定传入的什么类型的数据,所以除了null之外什么也不能add。但是get的时候是可以得到一个Number, 也就是上边界类型的数据的,因为不管存入什么数据类型都是Number的子类型,得到这些就是一个父类引用指向子类对象。

(3)固定下边界的通配符

用来限定元素的类型必须得指定类的父类(包括指定类和指定类的父类)

      
Copy
public static void addNumbers(List<? super Integer> list) { for (int i = 1; i <= 10; i++) { list.add(i); } } public static void main(String[] args) { List<Object> list1 = new ArrayList<>(); addNumbers(list1); System.out.println(list1); List<Number> list2 = new ArrayList<>(); addNumbers(list2); System.out.println(list2); List<Double> list3 = new ArrayList<>(); addNumbers(list3); // 编译报错 }

List<? super E>是能够调用add方法的,因为我们在addNumbers所add的元素就是Integer类型的,而传入的list不管是什么,都一定是Integer或其父类泛型的List,这时add一个Integer元素是没有任何疑问的。但是, 我们不能使用get方法,因为我们所传入的类都是Integer的类或其父类,所传入的数据类型可能是Integer到Object之间的任何类型,这是无法预料的,也就无法接收。唯一能确定的就是Object,因为所有类型都是其子类型。

泛型的限制和规则

(1)泛型的类型参数只能是引用类型,不能使用值类型(是不会自动装箱的)。

(2)泛型的类型参数可以有多个。

(3)泛型前后类型必须得要保持一致,即 ArrayList list = new ArrayList (); 从JAVA7开始,后面的类型可以不写 ArrayList list = new ArrayList<>(); 菱形语法。

(4)泛型类不是真正存在的类,不能使用instanceof运算符。

(5)泛型类的类型参数不能用在静态申明。

(6)如果定义了泛型,不指定具体类型,泛型默认指定为Ojbect类型。

(7)泛型使用?作为类型通配符,表示未知类型,可以匹配任何类型。因为是未知,所以无法添加元素。

(8)类型通配符上限:<? extends T>,?代表是T类型本身或者是T的子类型。常用于泛型方法,避免类型转换。

(9)类型通配符下限。<? super T>,?代表T类型本身或者是T的父类型。
除了通配符可以实现限制,类、接口和方法中定义的泛型参数也能限制上限和下限。

泛型擦除

泛型擦除:把泛型给去掉。

      
Copy
// 泛型擦除(把泛型给去掉) List<String> list = new ArrayList<>(); list.add("aa"); List list2 = null; list2 = list; // 把list当中的泛型给擦除掉 list2.add(10); list2.add("bb"); System.out.println(list2);
posted @ 2019-08-04 15:06 Lomen~ 阅读( ...) 评论( ...) 编辑 收藏

Guess you like

Origin www.cnblogs.com/xzh0717/p/11298284.html