Annotation and enumeration essence (JDA decompilation)

annotation

Classes, interfaces, enumerations, and annotations in Java can all be regarded as class types. Use jad to see what @interface is converted to:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Foo{
  String[] value();
  boolean bar();
}

Looking at the decompiled code shows that:

  • The custom annotation class Foo is converted to the interface Foo and inherits the Annotation interface
  • The value() and bar() in the original custom interface are converted into abstract methods
import java.lang.annotation.Annotation;

public interface Foo
    extends Annotation
{
    public abstract String[] value();

    public abstract boolean bar();
}

Annotations are usually used in conjunction with reflection, and since the custom annotations are finally converted into interfaces, and the attributes in the annotations are converted into abstract methods in the interface, then the interface instance is obtained after reflection, and the corresponding interface instance can naturally be called through the interface instance. Abstract method:

import java.util.Arrays;

@Foo(value={"sherman", "decompiler"}, bar=true)
public class Demo{
    public static void main(String[] args) {
        Foo foo = Demo.class.getAnnotation(Foo.class);
        System.out.println(Arrays.toString(foo.value())); // [sherman, decompiler]
        System.out.println(foo.bar());                    // true
    }
}

enumerate

The enum class is well understood by jad decompilation.

empty enumeration

First define an empty enumeration class:

public enum DummyEnum {
}

Use jad to decompile to see the results:

  • Custom enum classes are converted to final classes and inherit Enum
  • A private constructor with two parameters (name, odinal) is provided, and the constructor of the parent class is called. Note that this constructor will exist even if no parameters are provided, where name is the name of the enumeration instance, and odinal is the index number of the enumeration instance
  • An empty array $VALUES of a private static final custom type is initialized
  • Two public static methods are provided:
    • The values() method returns a shallow copy of the internal $VALUES via the clone() method. This method combined with the private constructor can perfectly implement the singleton pattern. Think about whether the values() method is similar to the getInstance() method in the singleton pattern.
    • valueOf(String s): call the valueOf method of the parent class Enum and cast it back
public final class DummyEnum extends Enum
{
	// The function is the same as the getInstance() method of the singleton mode
    public static DummyEnum[] values()
    {
        return (DummyEnum[])$VALUES.clone();
    }
	// Call the valueOf method of the parent class and return it
    public static DummyEnum valueOf(String s)
    {
        return (DummyEnum)Enum.valueOf(DummyEnum, s);
    }
	// By default, a private private two-parameter constructor is provided, and the constructor of the parent class Enum is called
    private DummyEnum(String s, int i)
    {
        super(s, i);
    }
	// Initialize a private static final empty array of this class
    private static final DummyEnum $VALUES[] = new DummyEnum[0];

}

enum containing abstract methods

An enum class can also contain abstract methods, but the enum instance must be defined and the abstract method immediately overridden, like this:

public enum DummyEnum {
    DUMMY1 {
        public void dummyMethod() {
            System.out.println("[1]: implements abstract method in enum class");
        }
    },

    DUMMY2 {
        public void dummyMethod() {
            System.out.println("[2]: implements abstract method in enum class");
        }
    };

    abstract void dummyMethod();

}

Then decompile to see what changes:

  • The original final class has become an abstract class: this is easy to understand, a class with abstract methods is naturally an abstract class
  • There are two more public static final members DUMMY1 and DUMMY2. The initialization process of these two instances is placed in the static code block, and the abstract method is directly rewritten in the instance process, similar to the form of an anonymous inner class.
  • The array **$VALUES[]** is initialized and put into the enumeration instance

Are there any other changes?

In the decompiled DummyEnum class, there is an abstract method, and the enum instance overrides the abstract method during the initialization process in the static code block. In Java, abstract methods and abstract method overriding are placed in one class at the same time, and can only be done in the form of inner classes. So the second point above should say that achievements are initialized in the form of inner classes.

You can take a look at the location where DummyEnum.class is stored. There should be two more files:

  • DummyEnum$1.class
  • DummyEnum$2.class

The .class file appears in Java symbol No surface Show Have Inside department kind live exist , At once picture O u t C l a s s The symbol indicates that there is an inner class, just like OutClass The symbol indicates that there is an inner class, just like OutClassInnerClass, the appearance of these two files also confirms the above statement of anonymous inner class initialization.

import java.io.PrintStream;

public abstract class DummyEnum extends Enum
{
    public static DummyEnum[] values()
    {
        return (DummyEnum[])$VALUES.clone();
    }

    public static DummyEnum valueOf(String s)
    {
        return (DummyEnum)Enum.valueOf(DummyEnum, s);
    }

    private DummyEnum(String s, int i)
    {
        super(s, i);
    }

	// abstract method
    abstract void dummyMethod();

	// Two public static final instances
    public static final DummyEnum DUMMY1;
    public static final DummyEnum DUMMY2;
    private static final DummyEnum $VALUES[];
 	
 	// static code block for initialization
    static 
    {
        DUMMY1 = new DummyEnum("DUMMY1", 0) {
            public void dummyMethod()
            {
                System.out.println("[1]: implements abstract method in enum class");
            }
        }
;
        DUMMY2 = new DummyEnum("DUMMY2", 1) {
            public void dummyMethod()
            {
                System.out.println("[2]: implements abstract method in enum class");
            }
        }
;
		// Initialize an array of this class
        $VALUES = (new DummyEnum[] {
            DUMMY1, DUMMY2
        });
    }
}

normal enum class

In actual development, the usual form of an enumeration class is a constructor with two parameters (int code, Sring msg), which can be returned as a status code. The Enum class actually provides a constructor that contains two parameters and is protected. In order to avoid ambiguity, set the constructor of the enum class to three and use jad to decompile:

The biggest change is that the current private constructor has changed from 2 parameters to 5, and internally the first two parameters are still passed to the parent class through super, and the remaining three parameters are the parameters that are really provided by themselves. It is conceivable that if the custom enumeration class only provides one parameter, the private constructor in the final generated underlying code should have three parameters, and the first two are still passed to the parent class through super.

public final class CustomEnum extends Enum
{
    public static CustomEnum[] values()
    {
        return (CustomEnum[])$VALUES.clone();
    }

    public static CustomEnum valueOf(String s)
    {
        return (CustomEnum)Enum.valueOf(CustomEnum, s);
    }

    private CustomEnum(String s, int i, int j, String s1, Object obj)
    {
        super(s, i);
        code = j;
        msg = s1;
        data = obj;
    }

    public static final CustomEnum FIRST;
    public static final CustomEnum SECOND;
    public static final CustomEnum THIRD;
    private int code;
    private String msg;
    private Object data;
    private static final CustomEnum $VALUES[];

    static 
    {
        FIRST = new CustomEnum("FIRST", 0, 10010, "first", Long.valueOf(100L));
        SECOND = new CustomEnum("SECOND", 1, 10020, "second", "Foo");
        THIRD = new CustomEnum("THIRD", 2, 10030, "third", new Object());
        $VALUES = (new CustomEnum[] {
            FIRST, SECOND, THIRD
        });
    }
}

Tags: Java

Posted by rockroka on Sun, 15 May 2022 23:53:51 +0300