java Language Advancement and Android Principle Kernel; Detailed Notes on Generic Principles

Keywords: Java Android Programming jvm

Preface;

java knowledge is the language foundation for Android development. Although kotlin has been introduced before, we still need to master java well for the following reasons:

1) SDK or java, kotlin also needs to be compiled to run in java;
2) At present, a large number of third-party libraries and inherited and predecessor codes are written by java.
3) Java language application is not only Android, but also the most popular language in background development.
4) Big company interviews require us to have a solid Java language base. Therefore, I hope that the majority of Android developers and friends do not despise the opportunity to improve their Java base

Generics play an important role in java and are widely used in object-oriented programming and various design patterns.

This paper is a detailed note on generic principles. I hope that we can give you a detailed introduction of generic principles. In the future, we will update and share a series of notes on java principles. We can continue to pay attention to them.

With more content, it is easier to absorb and understand by video detailed learning. Share at the end of the article

java Advancement and Android Kernel; Generic Details

I. What is generics? Why use generics?

Generics are "parameterized types". When it comes to parameters, the most familiar thing is to define a method with tangible parameters, and then pass arguments when calling the method. So how do you understand parameterized types? As the name implies, the type is parameterized from the original specific type, similar to the variable parameter in the method, in which case the type is also defined as a parameter form (which can be called a type parameter), and then the specific type (type parameter) is passed in when using/calling.

The essence of generics is to parameterize types (in the absence of creating new types, the types specified by generics are used to control the specific restrictions on parameters). That is to say, in the process of generic use, the data type of operation is designated as a parameter. This parameter type can be used in classes, interfaces and methods, which are called generic class, generic interface and generic method respectively.

2. To cite an example that has been cited countless times:

List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
    String item = (String)arrayList.get(i);
    Log.d("Generic testing","item = " + item);
}

There is no doubt that the program will end up crashing:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList can store any type. In the example, a String type is added, an Integer type is added, and then used as String, so the program crashes. In order to solve such problems (which can be solved at the compilation stage), generics came into being.

We'll change the code that initializes the list in the first line declaration, and the compiler will be able to help us find problems like this at the compilation stage.

List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); in the compilation phase, the compiler will report errors

III. GENERALITY CHARACTERISTICS

Generics are valid only at the compilation stage. Look at the following code:

List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();

Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();

if(classStringArrayList.equals(classIntegerArrayList)){
    Log.d("Generic testing","Same type");
}

Output: D / generic test: same type.

The above examples show that the program will take de-generic measures after compilation. That is to say, generics in Java are valid only at the compilation stage. In the compilation process, after correctly checking the generic results, the generic information will be erased, and the method of type checking and type conversion will be added at the boundary where the object enters and leaves the method. That is to say, generic information does not enter the runtime phase.

To sum up, generic types are logically considered to be multiple different types, but in fact they are all the same basic types.

IV. Use of generics

There are three ways to use generics: generic classes, generic interfaces, and generic methods.

generic class

Generic types are used in the definition of classes and are called generic classes. Generics allow you to open the same interface to a group of classes. The most typical container classes are List, Set, Map.

The most basic way to write generic classes (which may be a little dizzy, as detailed in the following examples):

Class class name < generic identifier: you can write any identifier to identify the type of the specified generic type >{
  private generic identifier /* (member variable type)*/ var; 
  .....

  }
}

One of the most common generic classes:

//Here T can be written as an arbitrary identifier. Common parameters such as T, E, K, V are often used to represent generics.
//When instantiating generic classes, you must specify the specific type of T
public class Generic<T>{ 
    //The member variable key is of type T, and the type of T is specified externally.  
    private T key;

    public Generic(T key) { //The type of the generic constructor parameter key is also T, and the type of T is specified externally.
        this.key = key;
    }

    public T getKey(){ //The return value type of the generic method getKey is T, and the type of T is specified externally.
        return key;
    }
}
//Generic type parameters can only be class types (including custom classes), not simple types.
//The incoming argument type needs to be the same as the generic type parameter type, namely Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);

//The incoming argument type needs to be the same as the generic type parameter type, namely String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("Generic testing","key is " + genericInteger.getKey());
Log.d("Generic testing","key is " + genericString.getKey());
12-27 09:20:04.432 13063-13063/?D/generic test: keyis 123456
 12-27 09:20:04.432 13063-13063/?D/generic test: key is key_vlaue

Do generic class definitions have to pass in generic type arguments? This is not the case. If a generic parameter is passed in when using a generic type, it will be restricted accordingly according to the incoming generic parameter, and then the generic type will play its due role. If no generic type argument is passed in, the type defined by the generic method or member variable in the generic class can be any type.

Look at an example:

Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);

Log.d("Generic testing","key is " + generic.getKey());
Log.d("Generic testing","key is " + generic1.getKey());
Log.d("Generic testing","key is " + generic2.getKey());
Log.d("Generic testing","key is " + generic3.getKey());
D/generic testing: key is 111111111
 D/generic testing: key is 4444
 D/generic testing: key is 55.55
 D/generic testing: key is false

Be careful:
Generic type parameters can only be class types, not simple types.
The instanceof operation cannot be used for exact generic types. The following operations are illegal and errors occur during compilation.

if(ex_num instanceof Generic<Number>){   
} 

generic interface

Generic interfaces and generic classes are basically the same in definition and use. Generic interfaces are often used in various classes of producers. Here's an example:

//Define a generic interface
public interface Generator<T> {
    public T next();
}

When a class that implements a generic interface does not pass in a generic argument:

/**
 * When a generic argument is not passed in, it is the same definition as a generic class. When declaring a class, you need to add the generic declaration to the class as well.
 * That is: class FruitGenerator < T > implements Generator < T >{
 * If generics are not declared, such as class FruitGenerator implements Generator < T >, the compiler will report an error: "Unknown class"
 */
class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}

When a class that implements a generic interface passes in a generic argument:

/**
 * When a generic argument is introduced:
 * Define a producer to implement this interface, although we only created a generic interface Generator < T >
 * But we can pass in countless arguments for T and form countless types of Generator interfaces.
 * When implementing a generic interface for a class, if a generic type has been passed into an argument type, all places where generics are used are replaced by the incoming argument type.
 * That is: Generator < T >, public T next(); all T in it are replaced by the incoming String type.
 */
public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

Generic wildcards

We know that Ingeter is a subclass of Number, and in the feature section we have also verified that Generic < Ingeter > and Generic < Number > are actually the same basic types. So the question arises. In the method of using Generic < Number > as the parameter, can we use the instance of Generic < Ingeter > to pass in? Are generic types logically similar to Generic < Number > and Generic < Ingeter > considered to be paternal?

To clarify this problem, we use Generic < T > as a generic class to continue with the following examples:

public void showKeyValue1(Generic<Number> obj){
    Log.d("Generic testing","key value is " + obj.getKey());
}
Generic<Integer> gInteger = new Generic<Integer>(123);
Generic<Number> gNumber = new Generic<Number>(456);

showKeyValue(gNumber);

// showKeyValue is a method compiler that will report an error for us: Generic < java.lang.Integer>. 
// cannot be applied to Generic<java.lang.Number>
// showKeyValue(gInteger);

From the prompt information, we can see that Generic < Integer > can not be regarded as a subclass of `Generic < Number'. It can be seen that the same generic type can correspond to multiple versions (because the parameter type is uncertain), and different versions of generic class instances are incompatible.

Back to the above examples, how to solve the above problems? You can't always define a new way to deal with classes of Generic < Integer > type, which is obviously contrary to many concepts in java. So we need a reference type that logically represents both Generic < Integer > and Generic < Number > parent classes. This type of wildcard came into being.

We can change the above method:

public void showKeyValue1(Generic<?> obj){
    Log.d("Generic testing","key value is " + obj.getKey());
}

Type wildcards are commonly used? Instead of specific type arguments, notice, here'?' It is a type argument, not a type parameter. Important to say three times! Here'?' Type argument, not type parameter! Here'?' Type argument, not type parameter! What's more straightforward is, what's here? Like Number, String, Integer, it's a real type, can you? Think of it as the parent of all types. It's a real type.

This wildcard can be used when the specific type is uncertain; when the type of operation is not required to use the specific functions of the type, only the functions in the Object class can be used. Then you can use? Wildcards to represent unknown types.

generic method

In java, the definition of generic classes is very simple, but the generic method is more complex.

In particular, most of the member methods in generic classes we have seen also use generics, and some even contain generic methods in generic classes, so it is very easy for beginners to misunderstand generic methods.

A generic class specifies the specific type of a generic type when instantiating a class; a generic method specifies the specific type of a generic type when calling a method.

/**
 * Basic introduction of generic methods
 * @param tClass Input generic argument
 * @return T The return value is of type T
 * Explain:
 *     1)public The < T > between the return value and the return value is very important and can be understood as declaring this method generic.
 *     2)The generic method is only the method that declares < T > and the member method that uses generic type in the generic class is not the generic method.
 *     3)<T>It indicates that generic type T will be used in the method, and then generic type T can be used in the method.
 *     4)Like the definition of generic classes, T can be written as arbitrary identifiers here. Common parameters such as T, E, K, V are often used to represent generics.
 */
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
  IllegalAccessException{
        T instance = tClass.newInstance();
        return instance;
}
Object obj = genericMethod(Class.forName("com.test.test"));
  • Basic usage of generic methods
    Looking at the examples above, some students may still be very confused. Let's summarize my generic method through another example.
public class GenericTest {
   //This class is a generic class, as described above.
   public class Generic<T>{     
        private T key;

        public Generic(T key) {
            this.key = key;
        }

        //What I want to say is actually this. Although generics are used in the method, this is not a generic method.
        //This is just a normal member method in a class, except that its return value is to declare a generic type that the generic class has declared.
        //So we can continue to use the generic T in this method.
        public T getKey(){
            return key;
        }

        /**
         * This method is obviously problematic, and the compiler will prompt us with the error message "cannot reslove symbol E"
         * Because generic E is not declared in the class declaration, the compiler cannot recognize it when using E as a parameter and return value type.
        public E setKey(E key){
             this.key = keu
        }
        */
    }

    /** 
     * This is a true generic approach.
     * Firstly, <T> between public and return value is essential, which indicates that this is a generic method and declares a generic T.
     * This T can appear anywhere in this generic method.
     * The number of generics can also be arbitrary 
     *    For example: public < T, K > K showKey Name (Generic < T > container){
     *        ...
     *        }
     */
    public <T> T showKeyName(Generic<T> container){
        System.out.println("container key :" + container.getKey());
        //Of course, this example is not very appropriate, just to illustrate the characteristics of generic methods.
        T test = container.getKey();
        return test;
    }

    //This is not a generic method, it is a common method, just using Generic < Number > as a parameter.
    public void showKeyValue1(Generic<Number> obj){
        Log.d("Generic testing","key value is " + obj.getKey());
    }

    //This is not a generic method, it is also a common method, but the use of generic wildcards?
    //It also confirms that the generic wildcard section describes? As a type argument, which can be regarded as the parent class of all classes such as Number.
    public void showKeyValue2(Generic<?> obj){
        Log.d("Generic testing","key value is " + obj.getKey());
    }

     /**
     * This method is problematic, and the compiler will prompt us with an error message: "UnKnown class'E'"
     * Although we have declared <T>, it also shows that this is a generic method that can handle generic types.
     * However, only generic type T is declared, and generic type E is not declared, so the compiler does not know how to deal with this type.
    public <T> T showKeyName(Generic<E> container){
        ...
    }  
    */

    /**
     * This method is also problematic, and the compiler will prompt us with an error message: "UnKnown class'T'"
     * For compilers, the type T is not declared in the project, so compilation does not know how to compile the class.
     * So this is not a correct generic method declaration.
    public void showkey(T genericObj){

    }
    */

    public static void main(String[] args) {


    }
}
  • Generic methods in classes
    Of course, this is not the whole of generic methods. Generic methods can be used anywhere and in any scenario. But there is a very special case, when generic methods appear in generic classes, let's take another look at an example.
public class GenericFruit {
    class Fruit{
        @Override
        public String toString() {
            return "fruit";
        }
    }

    class Apple extends Fruit{
        @Override
        public String toString() {
            return "apple";
        }
    }

    class Person{
        @Override
        public String toString() {
            return "Person";
        }
    }

    class GenerateTest<T>{
        public void show_1(T t){
            System.out.println(t.toString());
        }

        //A generic method is declared in the generic class, using generic E, which can be of any type. It can be the same type as T, or it can be different.
        //Because generic methods declare generics < E > when declared, the compiler can correctly identify generics recognized in generic methods even if generics are not declared in generic classes.
        public <E> void show_3(E t){
            System.out.println(t.toString());
        }

        //A generic method is declared in a generic class, using generic T. Note that this T is a completely new type and can not be the same type as the T declared in a generic class.
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        Person person = new Person();

        GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
        //apple is a subclass of Fruit, so here you can
        generateTest.show_1(apple);
        //The compiler will report an error because the generic type argument specifies Fruit and the incoming argument class is Person.
        //generateTest.show_1(person);

        //Both methods can be used successfully.
        generateTest.show_2(apple);
        generateTest.show_2(person);

        //Both methods can be used successfully.
        generateTest.show_3(apple);
        generateTest.show_3(person);
    }
}
  • Generic Method and Variable Parameters
    Let's look at an example of generic methods and variable parameters:
public <T> void printMsg( T... args){
    for(T t : args){
        Log.d("Generic testing","t is " + t);
    }
}
printMsg("111",222,"aaaa","2323.4",55.55);
  • Static methods and generics
    Static methods have one thing to note: static methods in classes use generics: static methods cannot access generics defined on classes; if the reference data type of static method operations is uncertain, generics must be defined in methods.

That is, if a static method is to use generics, it must also be defined as a generic method.

public class StaticGenerator<T> {
    ....
    ....
    /**
     * If you define a static method that uses generics in a class, you need to add additional generic declarations (define this method as a generic method)
     * Even static methods cannot use generics already declared in generic classes.
     * For example: public static void show (T) {.}, the compiler will prompt the error message:
          "StaticGenerator cannot be refrenced from static context"
     */
    public static <T> void show(T t){

    }
}
  • Summary of generic methods
    Generic methods enable methods to change independently of classes. The following are basic guidelines:

Whenever you can, you should try to use generic methods. That is to say, if the generic method is used to generize the whole class, then the generic method should be used. In addition, for a static method, the parameters of the generic type cannot be accessed. So if the static method wants to use generic ability, it must become a generic method.

  • Upper and lower boundaries of generics
    When using generics, we can also restrict the upper and lower bounds of incoming generic type arguments, such as: type arguments are only allowed to pass in a certain type of parent or a certain type of subclass.

Adding upper boundaries to generics means that the incoming type argument must be a subtype of the specified type.

 public void showKeyValue1(Generic<? extends Number> obj){
    Log.d("Generic testing","key value is " + obj.getKey());
}
Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Float> generic3 = new Generic<Float>(2.4f);
Generic<Double> generic4 = new Generic<Double>(2.56);

//This line of code compiler will prompt errors because String type is not a subclass of Number type
//showKeyValue1(generic1);

showKeyValue1(generic2);
showKeyValue1(generic3);
showKeyValue1(generic4);

If we change the definition of generic classes as well:

public class Generic<T extends Number>{
    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}
//This line of code also reports errors because String is not a subclass of Number
Generic<String> generic1 = new Generic<String>("11111");

Another example of generic methods is:

//When upper and lower boundary restrictions are added to generic methods, upper and lower boundaries must be added to < T > between permission declarations and return values, that is, when generic declarations are added.
//Public < T > T showKey Name (Generic < T extends Number > container), the compiler will report an error: "Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
    System.out.println("container key :" + container.getKey());
    T test = container.getKey();
    return test;
}

From the above two examples, we can see that the upper and lower boundaries of generics must be added together with the declaration of generics.

A note on generic arrays

See many articles will mention generic arrays, after looking at sun's documentation, in java is "can not create an exact generic type of array".

That is to say, the following example is not possible:

List<String>[] ls = new ArrayList<String>[10];  

It is possible to create generic arrays using wildcards, as follows:

List<?>[] ls = new ArrayList<?>[10];  

This is also possible:

List<String>[] ls = new ArrayList[10];

Here's an example of Sun's document to illustrate this problem:

List<String>[] lsa = new List<String>[10]; // Not really allowed.    
Object o = lsa;    
Object[] oa = (Object[]) o;    
List<Integer> li = new ArrayList<Integer>();    
li.add(new Integer(3));    
oa[1] = li; // Unsound, but passes run time store check    
String s = lsa[1].get(0); // Run-time error: ClassCastException.

In this case, because of the erasure mechanism of JVM generics, the JVM does not know the generic information at run time, so it can assign an ArrayList to oa[1] without exception, but it needs to do a type conversion when the data is extracted, so ClassCastException will appear. If the generic array declaration can be made, the situation mentioned above will not occur at compile time. Any warnings and errors will occur only at runtime.

Restricting the declaration of generic arrays, in this case, can prompt code at compile time for type safety issues, much better than without any prompts.

The following is allowed by wildcards: the type of an array cannot be a type variable unless it is wildcards, because for wildcards, the final data is extracted by explicit type conversion.

List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.    
Object o = lsa;    
Object[] oa = (Object[]) o;    
List<Integer> li = new ArrayList<Integer>();    
li.add(new Integer(3));    
oa[1] = li; // Correct.    
Integer i = (Integer) lsa[1].get(0); // OK 

Five. Conclusion

The examples in this paper are simply given to illustrate some of the ideas in generics, and do not necessarily have practical usability. In addition, when it comes to generics, I believe that the most commonly used is in collections. In fact, in the actual programming process, I can use generics to simplify development, and can guarantee the quality of code.

Java and Android Kernel Principles Technical Outline;


Sixth, BAT mainstream Android architecture technology outline + learning route + full set of videos;

See this article for details of architecture technology and learning routes and information sharing. Advanced Architecture Technology of Android, the Mainstream of BATJ Front Line Factory; System Details + Learning Route
(Including custom controls, NDK, architecture design, React native (Weex), performance optimization, complete business project development, etc.)

  • Ali P8 Android Architect Technical Brain Map;
  • Full set of high-level architecture video; seven mainstream technology modules, Video + source code + Notes

Posted by Aureole on Fri, 17 May 2019 22:59:51 -0700