junit, reflection and enumeration

11 minute read

Published:

Java basics – junit, reflection and enumeration

Junit, Reflection and Enumeration

Junit Unit Test

  • Test classification
    1. Black box testing: Give the input value to see if the program can give the expected output value
    2. White box testing: focus on the specific execution process of the program
  • Junit use: white box testing

    Steps for usage:

    1. Define the test class (test case)

      Suggest:

      Test class name = tested class name + Test

      Package name: xxx.xxx.xxx.test

    2. Define the test method (can be run separately)

      Suggest:

      Method name: test+test method name

      Return value: void

      Parameter list: empty parameters

    3. Add @Test annotation to the method

    4. Import Junit dependencies

    Result judgment: red failure, green success

    Use assertions for judgment

    @Test
        public void addTest(){
            Calculator c=new Calculator();
            int result = c.add(1, 2);
            Assert.assertEquals(3,result);
        }
    
  • Supplement:
    1. @Before: The modified method will be automatically executed before the test method
    2. @After: The modified method will be automatically executed after the test method

Reflection

  • Framework: semi-finished software, which can be developed on the basis of the framework to simplify coding

  • Reflection: a mechanism to encapsulate the various components of a class into other objects

    benefit:

    1. These objects can be manipulated while the program is running

    2. It can be decoupled to improve the scalability of the program

  • How to get the Class object

    1. Class.forName(“full class name”): Load the bytecode file into memory and return the Class object (mostly used for configuration files)
    2. Class name.class: Obtained by the attribute class of the class name (mostly used for parameter passing)
    3. Object.getClass(): Obtained by the getClass method of the object, and the getClass() method is defined in the Object class (mostly used to obtain the bytecode of the object)

    Note: The same bytecode file will only be loaded once during a run, and the Class object obtained by any method is the same

  • Common methods of Class objects

    1. Get features

      • Get member variables

        Field[] getFields(): Get all public modified member variables

        Field getField(String name)

        Field[] getDeclaredFields(): Get all member variables, regardless of modifiers

        Field getDeclaredField(String name)

      • Get construction method

        Constructor<?>[] getConstructors(): Get all public modified constructors

        Constructor<T> getConstructor(Class<?>... parameterTypes)

        Constructor<?>[] getDeclaredConstructors(): Get all construction methods, regardless of modifiers

        Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

      • Get member method

        Method[] getMethods(): Get all public modified member methods

        Method getMethod(String name, class<?>... parameterTypes)

        Method[] getDeclaredMethods(): Get all members, regardless of modifiers

        Method getDeclaredMethod(String name, class<?>... parameterTypes)

      • Get class name

        String getName()

    2. Setting function

      • Field member variables

        Set value: void set(Object obj,Object value)

        Get the value: Object get(Object obj)

        Ignore the security check of access permissions: setAccessible(true) (violent reflection)

      • Constructor construction method

        Create an object: T newInstance(Object... initargs)

        If you use the null parameter construction method, it can be simplified to the newInstance method of the Class object

      • Method method object

        Execution method: Object invoke(Object obj, Object... args)

        Get method name: String getName()

Annotation

  • Concept: a code-level description, a new feature after JDK1.5+
  • Use: @note name
  • Function classification:
    1. Write documentation: Generate documentation from metadata in the code
    2. Code analysis: analyze the code through the metadata in the code
    3. Compilation check: through the metadata in the code to enable the compiler to implement basic compilation checks
  • Predefined annotations in JDK
    1. @Override: Check whether the method is inherited from the parent class (interface)
    2. @Deprecated: The content marked by the annotation is out of date
    3. @SuppressWarnings: Suppress warnings (usually pass the parameter “all” @SuppressWarnings(“all”))
  • Custom annotation

    format:

    1. Meta annotations
    2. public @interface annotation name {attribute list}

    Essence: is an interface that inherits the Annotation interface

    public interface annotation name extends java.lang.annotation.Annotation

    Attributes: abstract methods in the interface

    note:

    1. The return value type of the attribute can only be:

      Basic data type

      String

      enumerate

      annotation

      Array of the above type

    2. The attribute is defined, and the attribute must be assigned when using it (method name=attribute value, multiple values are separated by commas)

      If the default keyword is used to specify the default initialization value when defining the attribute, the attribute assignment can be omitted when using the annotation

      If there is only one attribute to be assigned, and the name is value, the value can be omitted and the value can be defined directly

    Meta-annotation: An annotation that describes an annotation

    1. @Target: Describe the position where the annotation can be used {ElementType.TYPE (class), ElementType.METHOD (method), ElementType.FIELD (member variable)}
    2. @Retention: Describe the stage at which annotations can be retained {RetentionPolicy.RUNTIME (retained in the bytecode file and read by the JVM), RetentionPolicy.CLASS (retained in the bytecode file but not read by the JVM) ), RetentionPolicy.SOURCE (not retained in the bytecode file)}
    3. @Documented: Whether the annotation is extracted into the API document
    4. @Inherited: Describe whether the annotation is inherited by subclasses
  • Use annotations in the program

    1. Get the object (Class, Method, Field) where the annotation is defined

    2. Get the specified annotation

      getAnnotation(Class)

      In fact, a subclass implementation object of the annotation interface is generated in memory

    3. Call the abstract method in the annotation to get the configured property value

Enumeration

This article is translated by JavaGuide, public number: JavaGuide, original address: https://www.baeldung.com/a-guide-to-java-enums.

Please indicate the above paragraph for reprinting.

Reprinted from: https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/basic/%E7%94%A8%E5%A5%BDJava%E4%B8%AD%E7%9A% 84%E6%9E%9A%E4%B8%BE%E7%9C%9F%E7%9A%84%E6%B2%A1%E6%9C%89%E9%82%A3%E4%B9%88% E7%AE%80%E5%8D%95.md

Overview

In this article, we will see what Java enumerations are, what problems they solve, and how to use Java enumerations to implement some design patterns in practice.

The enum keyword was introduced in java5 and represents a special type of class, which always inherits the java.lang.Enum class. For more content, you can check its official document.

Enumerations are often compared with constants, probably because we actually use enumerations in large numbers to replace constants. So what are the advantages of this approach?

Constants defined in this way make the code more readable, allow compile-time checks, pre-record a list of acceptable values, and avoid unexpected behavior due to invalid values passed in.

The following example defines the status of a simple enumerated type pizza order. There are three states: ORDERED, READY, and DELIVERED:

package shuang.kou.enumdemo.enumtest;

public enum PizzaStatus {
    ORDERED,
    READY,
    DELIVERED;
}

Simply put, we avoid defining constants through the above code. We put all constants related to the status of pizza orders into an enumerated type.

System.out.println(PizzaStatus.ORDERED.name());//ORDERED
System.out.println(PizzaStatus.ORDERED);//ORDERED
System.out.println(PizzaStatus.ORDERED.name().getClass());//class java.lang.String
System.out.println(PizzaStatus.ORDERED.getClass());//class shuang.kou.enumdemo.enumtest.PizzaStatus

Custom enumeration method

Now that we have a basic understanding of what enumerations are and how to use them, let us define some additional A on the enumeration The PI method takes the previous example to a new level:

public class Pizza {
    private PizzaStatus status;
    public enum PizzaStatus {
        ORDERED,
        READY,
        DELIVERED;
    }
 
    public boolean isDeliverable() {
        if (getStatus() == PizzaStatus.READY) {
            return true;
        }
        return false;
    }
     
    // Methods that set and get the status variable.
}

Use == to compare enumerated types

Since the enumeration type ensures that only one constant instance exists in the JVM, we can safely use the “==” operator to compare two variables, as shown in the above example; in addition, the “==” operator can provide compile-time and runtime Security.

First, let’s take a look at runtime security in the following code snippet, where the “==” operator is used to compare states, and if both values are null, NullPointerException will not be thrown. On the contrary, if the equals method is used, a NullPointerException will be thrown:

if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED));
if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED);

For compile-time safety, let’s look at another example. Two different enumeration types are compared. The result of the comparison using the equal method is determined to be true, because the enumeration value of the getStatus method is consistent with another type enumeration value, but logically it should Is false. This problem can be avoided by using the == operator. Because the compiler will indicate type incompatibility error:

if(testPz.getStatus().equals(TestColor.GREEN));
if(testPz.getStatus() == TestColor.GREEN);

Use enumerated types in switch statements

public int getDeliveryTimeInDays() {
    switch (status) {
        case ORDERED: return 5;
        case READY: return 2;
        case DELIVERED: return 0;
    }
    return 0;
}

Enumeration type properties, methods and constructors

You can make it more powerful by defining attributes, methods and constructors in the enumeration type.

Now, let us extend the above example to realize the transition from one stage of pizza to another, and learn how to get rid of the if statement and switch statement used before:

public class Pizza {
 
    private PizzaStatus status;
    public enum PizzaStatus {
        ORDERED (5){
            @Override
            public boolean isOrdered() {
                return true;
            }
        },
        READY (2){
            @Override
            public boolean isReady() {
                return true;
            }
        },
        DELIVERED (0){
            @Override
            public boolean isDelivered() {
                return true;
            }
        };
 
        private int timeToDelivery;
 
        public boolean isOrdered() {return false;}
 
        public boolean isReady() {return false;}
 
        public boolean isDelivered(){return false;}
 
        public int getTimeToDelivery() {
            return timeToDelivery;
        }
 
        PizzaStatus (int timeToDelivery) {
            this.timeToDelivery = timeToDelivery;
        }
    }
 
    public boolean isDeliverable() {
        return this.status.isReady();
    }
 
    public void printTimeToDeliver() {
        System.out.println("Time to delivery is "+
          this.getStatus().getTimeToDelivery());
    }
     
    // Methods that set and get the status variable.
}

The following code shows how it works:

@Test
public void givenPizaOrder_whenReady_thenDeliverable() {
    Pizza testPz = new Pizza();
    testPz.setStatus(Pizza.PizzaStatus.READY);
    assertTrue(testPz.isDeliverable());
}

EnumSet and EnumMap

  • EnumSet

EnumSet is a Set type specially designed for enumerated types.

Compared with HashSet, it is a very efficient and compact representation of a specific Enum constant set due to the use of internal bit vector representation.

It provides a type-safe alternative to the traditional int-based “bit flags”, allowing us to write concise code that is easier to read and maintain.

EnumSet is an abstract class, which has two implementations: RegularEnumSet and JumboEnumSet, which one you choose depends on the number of constants in the enum at the time of instantiation.

In many scenarios, the enumeration constant set operations (such as: subset, add, delete, containsAll and removeAll batch operations) use EnumSet very suitable; if you need to iterate all possible constants, use Enum. values().

public class Pizza {
 
    private static EnumSet<PizzaStatus> undeliveredPizzaStatuses =
      EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY);
 
    private PizzaStatus status;
 
    public enum PizzaStatus {
        ...
    }
 
    public boolean isDeliverable() {
        return this.status.isReady();
    }
 
    public void printTimeToDeliver() {
        System.out.println("Time to delivery is "+
          this.getStatus().getTimeToDelivery() + "days");
    }
 
    public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) {
        return input.stream().filter(
          (s) -> undeliveredPizzaStatuses.contains(s.getStatus()))
            .collect(Collectors.toList());
    }
 
    public void deliver() {
        if (isDeliverable()) {
            PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy()
              .deliver(this);
            this.setStatus(PizzaStatus.DELIVERED);
        }
    }
     
    // Methods that set and get the status variable.
}

The following test demonstrates the powerful function of EnumSet in certain scenarios:

@Test
public void givenPizaOrders_whenRetrievingUnDeliveredPzs_thenCorrectlyRetrieved() {
    List<Pizza> pzList = new ArrayList<>();
    Pizza pz1 = new Pizza();
    pz1.setStatus(Pizza.PizzaStatus.DELIVERED);
 
    Pizza pz2 = new Pizza();
    pz2.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz3 = new Pizza();
    pz3.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz4 = new Pizza();
    pz4.setStatus(Pizza.PizzaStatus.READY);
 
    pzList.add(pz1);
    pzList.add(pz2);
    pzList.add(pz3);
    pzList.add(pz4);
 
    List<Pizza> undeliveredPzs = Pizza.getAllUndeliveredPizzas(pzList);
    assertTrue(undeliveredPzs.size() == 3);
}
  • EnumMap

EnumMap is a specialized mapping implementation that uses enum constants as keys. Compared with the corresponding HashMap, it is an efficient and compact implementation, and is represented as an array internally:

EnumMap<Pizza.PizzaStatus, Pizza> map;

Let’s take a quick look at a real example that demonstrates how to use it in practice:

public static EnumMap<PizzaStatus, List<Pizza>> 
  groupPizzaByStatus(List<Pizza> pizzaList) {
    EnumMap<PizzaStatus, List<Pizza>> pzByStatus = 
      new EnumMap<PizzaStatus, List<Pizza>>(PizzaStatus.class);
     
    for (Pizza pz : pizzaList) {
        PizzaStatus status = pz.getStatus();
        if (pzByStatus.containsKey(status)) {
            pzByStatus.get(status).add(pz);
        } else {
            List<Pizza> newPzList = new ArrayList<Pizza>();
            newPzList.add(pz);
            pzByStatus.put(status, newPzList);
        }
    }
    return pzByStatus;
}

The following test demonstrates the powerful function of EnumMap in certain scenarios:

@Test
public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped() {
    List<Pizza> pzList = new ArrayList<>();
    Pizza pz1 = new Pizza();
    pz1.setStatus(Pizza.PizzaStatus.DELIVERED);
 
    Pizza pz2 = new Pizza();
    pz2.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz3 = new Pizza();
    pz3.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz4 = new Pizza();
    pz4.setStatus(Pizza.PizzaStatus.READY);
 
    pzList.add(pz1);
    pzList.add(pz2);
    pzList.add(pz3);
    pzList.add(pz4);
 
    EnumMap<Pizza.PizzaStatus,List<Pizza>> map = Pizza.groupPizzaByStatus(pzList);
    assertTrue(map.get(Pizza.PizzaStatus.DELIVERED).size() == 1);
    assertTrue(map.get(Pizza.PizzaStatus.ORDERED).size() == 2);
    assertTrue(map.get(Pizza.PizzaStatus.READY).size() == 1);
}