use of custom annotations
Asked Answered
E

5

26

I found several related (not duplicate) question to this, but they didn't satisfy me.

I am unable to understand where and why to use custom annotations?

I read an example of custom annotation in a book, but it was not explained thoroughly.

@interface MyAnno
{
    String str();
    int val();
}

class MyClass
{
    @MyAnno(str = "Annotation example", val = 100)
    public static void myMeth()
    {
        System.out.println("Inside myMeth()");
    }
}

class CustomAnno
{
    public static void main(String args[])
    {
        MyClass.myMeth();
    }
}

The output is as expected Inside myMeth().

I am having few questions regarding this example.

1- How can I use String str() and int val() in this program? OR

What is the use of any abstract method of an custom annotation?

2- Why custom annotations. I mean that what effect they are having on any code.

3- How can I create an annotation which is having effects like @override is having?(I mean any kind of effect which can be noticed)

If this example is useless for you, then please give me a suitable small example in which a custom annotation is used.

Empower answered 28/6, 2015 at 18:31 Comment(7)
Do you understand what annotations in general are for?Sciurine
@PM77-1 : To add supplement information in a source fileEmpower
Do you know what annotation processor is?Sciurine
The annotation is not really being used in your example - you can probably find better examples online.Articulation
@Articulation : I am searching for an example which is having an effect like override is having ( causes compile time error I the method is not overridden)Empower
@kevingomes That won't be possible - the behaviour of override is implemented by the compiler itself - you wouldn't be able to implement it yourself. See also: #31098055Articulation
@kevingomes I have modified your code a bit - check it out in the answer below in the new edit.Filings
S
13

Three main reasons to use custom annotations are:

  • To reduce the effort of writing code (a compile-time annotation processor generates code for you). Here is a tutorial: part 1, part 2.
  • To provide additional correctness guarantees (a compile-time annotation processor warns you about errors). One nice tool for this is the Checker Framework, which prevents null pointer dereferences, concurrency errors, and more.
  • To customize behavior (at run time, your code checks for the annotation using reflection and behaves differently depending on whether the annotation is present). Frameworks such as Hibernate use annotations this way; also see an Oracle article.

In each case, use of annotations reduces the likelihood of errors in your code, compared to other non-annotation approaches.

Supernova answered 29/6, 2015 at 1:20 Comment(0)
F
8

Here is a minimal example. The following code demonstrates use of custom annotation.

It is about Employees and Benefits. If we have a requirement such that BasicBenefits has to be applied to all types of employess then we can come up with custom annotation such as BasicBenefits, and annotate all types of Employee implementations (e.g. CorporateEmployee, ContractEmployee, ManagerEmployee etc. etc.) with the BasicBenefits.

Custom Annotation Class(interface)

import java.lang.annotation.*;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

@interface BasicBenefits {
    String bId() default "B-101";
    String bName() default "General Class A Employee";
}

Class using the custom annotation(no need of any imports):

@BasicBenefits(bId="B-400", bName="General Plus Class A Employee")
public class Employee {
    String eId;
    String eName;
    public Employee(String eId, String eName){
        this.eId = eId;
        this.eName = eName;
    }

    public void getEmployeeDetails(){
        System.out.println("Employee ID: "+eId);
        System.out.println("Employee Name: "+eName);
    }
}

Driver class to test out the above.

import java.lang.annotation.Annotation;
public class TestCustomAnnotationBasicBenefits {
    public static void main(String[] args) throws Exception{
        Employee emp = new Employee("E-100", "user3320018");
        emp.getEmployeeDetails();
        Class reflectedClass = emp.getClass();
        Annotation hopeBenefitAnn = reflectedClass.getAnnotation(BasicBenefits.class);
        BasicBenefits bBenefits = (BasicBenefits)hopeBenefitAnn;
        System.out.println("Benefit ID: "+bBenefits.bId());
        System.out.println("Benefit Name: "+bBenefits.bName());
    }
}

Your code look almost there, just two things need to be included in the main method.

1.) Need reference to MyClass 2.) Need to get the annotation using reflection from MyClass.

Here is a bit modified code from what you have:

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno
{
    String str();
    int val();
}

//using above custom annotation on class level
//can also use method level
//just need to change t @Target(ElementType.METHOD)
@MyAnno(str = "Annotation example", val = 100)
class MyClass
{

    public static void myMeth()
    {
        System.out.println("Inside myMeth()");
    }
}

import java.lang.annotation.Annotation;
class CustomAnno
{
    public static void main(String args[])
    {
        //1. getting reference to the class where the custom annotation is applied.
        //2. then getting the annotation to get the values 
        MyClass myClass = new MyClass();
        Class cls = myClass.getClass();
        Annotation getMyAnno = cls.getAnnotation(MyAnno.class);
        MyAnno myAnno = (MyAnno)getMyAnno;
        MyClass.myMeth(); //left this as is.
        System.out.println("myAnno.str(): "+ myAnno.str());
        System.out.println("myAnno.str(): "+ myAnno.val());     
    }
}
Filings answered 28/6, 2015 at 18:45 Comment(4)
Just printing out what we set in an annotation does not show what effect, if any, it has other than documentation, which is what OP is asking.Dziggetai
@Nirmal, Although it is a nice example, I don't see any useful use-case. Can you please share your thoughts?Berlinda
@Berlinda - this is useful in cases where you want to reduce lines of code by putting common ops in one place and refer to the use just by annotating. IMHO custom annotations is for organizing code and making it easy to read. As long as you are fine with the use case of certain custom annotation you put in place, you won’t read the implementation details of the annotated piece right?Filings
@Dziggetai it’s a bit late to respond on this but my purpose is to explain creation and use aspect of custom annotation which op has also hinted on asking.Filings
K
2

The abstract methods of the annotation define the values you can pass to it (in your case str = "Annotation example", val = 100). You can access them using reflection (Method.<T>getAnnotation(Class<T>)). Custom annotations don’t have direct impact. They are only useful if you evaluate them.

Note that you have to annotate your custom annotation with @Retention(value=RUNTIME) to be able to read it via reflection.

Kameko answered 28/6, 2015 at 18:36 Comment(3)
any example for a custom annotation which atleast do something. Like @override causes a compilation error is the method is not overriddenEmpower
You should add to your answer the "retention" requirement for the annotationColchicum
An example is JUnit’s @Test annotation. JUnit is looking for methods with that annotation via reflection and then executes them as test cases.Kameko
U
1

To be of any use, annotations must be parsed first. The built-in annotations (such as @Override or @FunctionalInterface, to name the most obvious ones) are parsed by the compiler itself. As for custom annotations, these guys are commonly parsed by third-party frameworks, although we can also use the reflection mechanism to demonstrate this technique in standalone code.

By way of an example, the code below changes its behaviour at run time depending on the value of the field declared in the custom annotation named @SwitchingAnnotation:

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface SwitchingAnnotation{
    boolean flag();
}

public class Worker{
    void doThis(){ System.out.println("Doing this"); }
    void doThat(){ System.out.println("Doing that"); }
    @SwitchingAnnotation(
        flag = false
    )
    public void work(boolean flag) {
        if (flag) doThis();
        else doThat();
    }
}

class Test{
    public static void main(String[] args) {
        try{
            SwitchingAnnotation sw = Worker.class.getMethod("work", boolean.class)
                                                 .getAnnotation(SwitchingAnnotation.class);

            new Worker().work(sw.flag());    // prints Doing that
        }
        catch(NoSuchMethodException nsme){
            System.out.println(nsme);
        }
    }
}
Uriiah answered 31/3, 2018 at 9:46 Comment(2)
What's the point of doing that though? Isn't that equivalent to having a public field variable 'flag' that you just directly mutate from the main method? Just waay more verbose; hence worse.Zoie
@SebastianNielsen You are absolutely correct: semantically they are close. In fact, as you recall, people used to follow the approach you suggested before annotations were introduced in JDK 5. However, from the class design point of view annotations are safer: public fields break encapsulation. Also, what if we need to reserve annotation-induced polymorphic behavior for the parent class only? non-private fields are inheritable, after all... Okay, perhaps a cleaner, more expressive example of annotation usage would have been with Repeating Annotations that appeared in Java 8.Uriiah
S
0

In my case, I tried using METHOD-level annotations. Below is the code for parsing the annotations.

`public static void main(String[] args) throws Exception {
            
            Framework myFramework = new Framework();
            System.out.println("==WithOut-Annotation Parsing==");
            myFramework.serviceCall();
            
            System.out.println("==Annotation Parsing==");
            myFramework.serviceCallx();
        }
    
    @Inherited
    @Documented
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomOne {
    
        String valueOne() default "valueOne";
    
        String valueTwo() default "valueTwo";
    
    }
    
    @Inherited
    @Documented
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomTwo {
    
        String valueThree() default "valueThree";
    
        String valueFour() default "valueFour";
    
    }
    public class Service {
        
        
        @CustomOne(valueOne = "One", valueTwo = "Two")
        @CustomTwo(valueThree = "Three", valueFour = "Four")
        public void test() {
    
            System.out.println("I am inside Service :: test() method");
    
        }
        
        
        @CustomOne
        @CustomTwo
        public void testx() {
            
            System.out.println("I am inside Service :: testx() method");
            
        }
    
    }
    
    public class Framework {
    
        // normal
        public void serviceCall() {
            Service service = new Service();
            service.test();
            service.testx();
        }
    
        // annotation parsing
        public void serviceCallx() throws Exception {
            
            Service service = new Service();
            Method method = service.getClass().getMethod("testx"); // give specific method in string format
    
            if (method.isAnnotationPresent(CustomOne.class)) {
                CustomOne customOne = method.getAnnotation(CustomOne.class);
                
                System.out.println("Custom One Annotation Parsing : " + customOne.valueOne() + " " + customOne.valueTwo());
            }
            if (method.isAnnotationPresent(CustomTwo.class)) {
                CustomTwo customTwo = method.getAnnotation(CustomTwo.class);
                
                System.out.println("Custom Two Annotation Parsing : " + customTwo.valueThree() + " " + customTwo.valueFour());
            }
    
        }
    
    }
    
    
    
    
    `
Sally answered 18/4 at 5:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.