Java Annotations: @Override, @Deprecated සහ Custom Annotations - SC Guide

Java Annotations: @Override, @Deprecated සහ Custom Annotations - SC Guide

මචංලා, Java Annotations: The Developer's Secret Weapon

කොහොමද ඉතින්? Java වල වැඩ කරනකොට අපිට ගොඩක් වෙලාවට හම්බවෙන පොඩි 'විශේෂ' දේවල් ටිකක් තියෙනවා. ඒ අතරින් අද අපි කතා කරන්න යන්නේ Java Annotations ගැන. මේවා අපේ code එකට අමතර තොරතුරු (metadata) එකතු කරන්න පුළුවන් powerful tools ටිකක්. සාමාන්‍යයෙන් මේවා code එක run වෙන විදියට කෙලින්ම බලපාන්නේ නැහැ, ඒත් compiler එකට, JVM එකට, frameworks වලට වගේම අපිම ලියන tools වලටත් මේවා හරිම ප්‍රයෝජනවත් වෙනවා. Code එක read කරන්න, maintain කරන්න සහ debugging කරන්නත් මේවා ගොඩක් උදව් වෙනවා. එහෙනම් අපි බලමු මොනවද මේ annotations කියන්නේ, මොනවද අපි නිතරම දකින built-in annotations, සහ අපිටම කොහොමද custom annotations හදාගන්නේ කියලා!

මොනවද මේ Annotations කියන්නේ?

සරලවම කියනවා නම්, Annotation එකක් කියන්නේ Java code එකට attach කරන metadata කොටසක්. මේවා පටන් ගන්නේ `@` symbol එකෙන්. හිතන්නකෝ අපි පොතක වැදගත් තැන් highlight කරනවා වගේ, code එකේ වැදගත් තැන් වලට notes දානවා වගේ දෙයක්. මේ notes වල තියෙන තොරතුරු compiler එකට, IDE එකට (IntelliJ IDEA, Eclipse වගේ), frameworks වලට (Spring, Hibernate වගේ) සහ runtime එකේදී අපිට reflection හරහා access කරන්න පුළුවන්. මේවා code එක compile වෙද්දී, run වෙද්දී, නැත්නම් code එක analyze කරන tools වලට වැදගත් තොරතුරු සපයනවා. මේවා code එකේ behavior එක කෙලින්ම වෙනස් කරන්නේ නැහැ, නමුත් code එකට අදාළ අමතර තොරතුරු රැගෙන යනවා.

උදාහරණයක් විදියට, Spring Framework එකේදී අපි @Controller, @Service, @Repository වගේ annotations පාවිච්චි කරනවා. මේවායින් Spring Framework එකට කියනවා අදාළ class එක මොන වගේ role එකක්ද කරන්නේ කියලා. ඒ වගේම, JUnit වල @Test annotation එක පාවිච්චි කරලා test methods identify කරනවා. මේ හැම එකකදීම annotations පාවිච්චි වෙන්නේ code එකට metadata එකතු කරන්නයි.

අපි නිතර දකින Built-in Annotations

Java වල built-in annotations කිහිපයක්ම තියෙනවා, ඒ අතරින් අපිට වැඩ කරනකොට නිතරම හම්බවෙන කිහිපයක් ගැන බලමු.

1. @Override

මේක තමයි අපි ගොඩක් වෙලාවට දකින annotation එක. @Override කියන්නේ අපි superclass එකක method එකක් override කරනවා කියලා compiler එකට කියන්න පාවිච්චි කරන එකක්. මේකෙන් වෙන වැදගත්ම දේ තමයි, අපි method signature එකේ වැරැද්දක් කළොත් (උදා: නම වැරදියට ටයිප් කළොත්, parameter type එකක් මාරු කළොත්), compiler එක අපිට error එකක් දෙනවා. නැත්නම්, අපි හිතන්නේ override කළා කියලා, ඒත් ඇත්තටම වෙලා තියෙන්නේ අලුත් method එකක් ලියවුණු එකක්. @Override තියෙන නිසා කෝකටත් තෛලය වගේ, වැරදි වෙන්න දෙන්නේ නෑ!

class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() { // Correctly overrides Animal's makeSound()
        System.out.println("Woof woof!");
    }

    // If you made a typo, compiler would complain:
    // @Override
    // public void makesound() { // Compiler error: Method does not override or implement a method from a supertype
    //     System.out.println("Woof woof!");
    // }
}

public class OverrideExample {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        myDog.makeSound(); // Outputs: Woof woof!
    }
}

2. @Deprecated

@Deprecated annotation එකෙන් කියන්නේ අදාළ program element (class, method, field) එක තවදුරටත් නිර්දේශ කරන්නේ නැහැ කියන එක. ඒ කියන්නේ ඒක obsolete වෙලා, අලුත් developments වලදී පාවිච්චි කරන්න එපා කියලා. මේකෙන් compiler එකට warning එකක් දෙන්න පුළුවන්, ඒ වගේම IDE එකේදී code එකට strikethrough එකක් දාලා පෙන්නනවා. අපි ලියපු code එකේ පැරණි කොටස් ඉවත් කරනවා නම්, මේ annotation එක දාලා අනිත් developer ලාට දැනුම් දෙන්න පුළුවන් අලුත් version එකට මාරු වෙන්න කියලා. මේක code base එක clean කරගන්න සහ maintain කරන්න හරිම වැදගත්.

class OldCalculator {
    @Deprecated
    public int add(int a, int b) { // This method is deprecated
        System.out.println("Using deprecated add method.");
        return a + b;
    }

    public int sum(int a, int b) { // New, preferred method
        System.out.println("Using new sum method.");
        return a + b;
    }
}

public class DeprecatedExample {
    public static void main(String[] args) {
        OldCalculator calculator = new OldCalculator();
        calculator.add(5, 3); // IDE/Compiler will show a warning about using a deprecated method
        calculator.sum(5, 3);
    }
}

3. @SuppressWarnings

සමහර වෙලාවට compiler එක දෙන warnings ටිකක් කරදරයක් වෙන්න පුළුවන්, විශේෂයෙන්ම ඒ warnings අපිට ඇත්තටම වැදගත් නැති වෙලාවට. @SuppressWarnings annotation එකෙන් පුළුවන් specific compiler warnings suppress කරන්න. මේක පරිස්සමින් පාවිච්චි කරන්න ඕනේ, මොකද අනවශ්‍ය විදියට warnings suppress කරන එක හොඳ practice එකක් නෙවෙයි. Warning එකක් suppress කරන්න කලින්, ඒකෙන් කියවෙන්නේ මොකක්ද කියලා හොඳට තේරුම් ගන්න එක වැදගත්.

import java.util.ArrayList;
import java.util.List;

public class SuppressWarningsExample {

    @SuppressWarnings("unchecked") // Suppresses the unchecked warning for this method
    public void addElementsToRawList() {
        List myList = new ArrayList(); // This would normally generate an "unchecked" warning
        myList.add("Hello");
        myList.add(123);
    }

    public static void main(String[] args) {
        SuppressWarningsExample example = new SuppressWarningsExample();
        example.addElementsToRawList();
        System.out.println("Warnings suppressed for addElementsToRawList method.");
    }
}

4. @FunctionalInterface

Java 8 එක්ක ආපු Lambda Expressions සහ Stream API එක්ක වැඩ කරන අයට මේක හරිම වැදගත්. @FunctionalInterface annotation එකෙන් කියන්නේ අදාළ interface එකට එක abstract method එකක් විතරයි තියෙන්නේ කියලා (SAM - Single Abstract Method). මේක compiler එකට කියනවා ඒ interface එක functional interface එකක් විදියට පාවිච්චි කරන්න පුළුවන් කියලා, ඒ වගේම අපිට වැරදිලා දෙවැනි abstract method එකක් දැම්මොත් error එකක් දෙනවා. මේක Java 8+ වලදී code එක clean කරගන්න සහ functional programming concepts Implement කරන්න උදව් වෙනවා.

@FunctionalInterface
interface MyProcessor {
    void process(String data);
    // void anotherMethod(); // If uncommented, compiler error as it's not a functional interface anymore
}

public class FunctionalInterfaceExample {
    public static void main(String[] args) {
        MyProcessor stringProcessor = (text) -> System.out.println("Processing: " + text);
        stringProcessor.process("Hello Java Annotations");
    }
}

තමන්ගේම Annotations හදමු! (Let's Create Our Own Annotations!)

Built-in annotations වලට අමතරව, අපිට පුළුවන් අපේම requirements වලට ගැලපෙන විදියට custom annotations හදාගන්න. මේවා frameworks හදනකොට, code generation tools හදනකොට, නැත්නම් අපේ application එකේ specific validation rules define කරනකොට හරිම ප්‍රයෝජනවත් වෙනවා.

Custom Annotation එකක් හදන්නේ කොහොමද?

Custom annotation එකක් හදන්න @interface කියන keyword එක පාවිච්චි කරනවා. මේක class එකක්, interface එකක් වගේමයි, හැබැයි මුලට @ එකතු වෙනවා.

public @interface MyCustomAnnotation {
    // Annotation elements go here
}

වැදගත් Meta-Annotations

අපේ custom annotations define කරනකොට, තවත් annotations කිහිපයක් පාවිච්චි කරන්න වෙනවා. මේවාට කියන්නේ meta-annotations කියලා.

  • @Retention: මේකෙන් කියනවා අදාළ annotation එක කොච්චර කල් retain වෙනවද කියලා. ප්‍රධාන options තුනක් තියෙනවා:
    • RetentionPolicy.SOURCE: Compiler එකෙන් discard කරනවා. Source code එක analyze කරන tools වලට විතරයි මේවා වැදගත් වෙන්නේ. (උදා: @Override, @SuppressWarnings)
    • RetentionPolicy.CLASS: Compile වෙද්දී .class file එකේ record වෙනවා, ඒත් runtime එකේදී available නෑ. (default)
    • RetentionPolicy.RUNTIME: Compile වෙද්දී .class file එකේ record වෙනවා වගේම, runtime එකේදී Reflection හරහා access කරන්නත් පුළුවන්. මේක තමයි frameworks වලට ගොඩක් වෙලාවට ඕන වෙන්නේ. (උදා: Spring, Hibernate)
  • @Target: මේකෙන් කියනවා අදාළ annotation එක කොහෙටද apply කරන්න පුළුවන් කියලා (class, method, field, parameter, etc.). Options කිහිපයක් තියෙනවා:
    • ElementType.TYPE: Class, interface, enum, annotation
    • ElementType.FIELD: Field (instance or static variable)
    • ElementType.METHOD: Method
    • ElementType.PARAMETER: Method parameter
    • ElementType.CONSTRUCTOR: Constructor
    • ElementType.LOCAL_VARIABLE: Local variable
    • ElementType.ANNOTATION_TYPE: Another annotation
    • ElementType.PACKAGE: Package
  • @Documented: මේ annotation එකෙන් කියනවා, අදාළ annotation එක Javadoc (Java documentation) එකට ඇතුළත් වෙන්න ඕනේ කියලා.
  • @Inherited: මේ annotation එක class එකකට apply කරලා තියෙනවා නම්, ඒ class එක extend කරන sub-classes වලටත් ඒ annotation එක inherit වෙනවා කියලා කියනවා.

Custom Annotation උදාහරණයක්: @DeveloperInfo

අපි හිතමු අපිට ඕනේ methods වලට developer කවුද, අන්තිමට modify කරපු දවස මොකක්ද, සහ ඒ method එක ගැන පොඩි description එකක් වගේ තොරතුරු එකතු කරන්න. ඒකට අපිට මෙන්න මේ වගේ custom annotation එකක් හදාගන්න පුළුවන්.

Annotation Definition:

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

@Retention(RetentionPolicy.RUNTIME) // This annotation will be available at runtime via Reflection
@Target(ElementType.METHOD) // This annotation can only be applied to methods
public @interface DeveloperInfo {
    String developerName() default "Unknown"; // Element with a default value
    String lastModified() default "N/A"; // Element with a default value
    String description(); // Required element (no default value)
}

මේ annotation එකට elements තුනක් තියෙනවා: developerName, lastModified, සහ description. මුල් දෙකට default values දීලා තියෙනවා, ඒ නිසා ඒවා apply කරනකොට අනිවාර්යයෙන්ම දෙන්න ඕනේ නැහැ. හැබැයි description එකට default value එකක් නැති නිසා ඒක අනිවාර්යයෙන්ම දෙන්න ඕනේ.

Annotation එක භාවිතා කරන හැටි:

class ProjectUtils {
    @DeveloperInfo(developerName = "Kamal Perera", lastModified = "2023-10-27", description = "Method to calculate overall project progress based on tasks.")
    public double calculateProgress() {
        // Imagine complex logic here to calculate progress
        return 0.75; // 75% complete
    }

    @DeveloperInfo(description = "Helper method to convert date format.") // Using default values for developerName and lastModified
    public String formatDate(String date) {
        // Date formatting logic
        return date.replace("-", "/");
    }
}

Reflection හරහා Annotation එක කියවන හැටි:

RetentionPolicy.RUNTIME විදියට define කරපු නිසා, අපිට පුළුවන් runtime එකේදී Reflection API එක පාවිච්චි කරලා මේ annotation වල තොරතුරු කියවන්න.

import java.lang.reflect.Method;

public class AnnotationReader {
    public static void main(String[] args) {
        try {
            // Get the Class object for ProjectUtils
            Class<ProjectUtils> projectUtilsClass = ProjectUtils.class;

            // Get the Method object for calculateProgress
            Method calculateProgressMethod = projectUtilsClass.getMethod("calculateProgress");

            // Check if the DeveloperInfo annotation is present on this method
            if (calculateProgressMethod.isAnnotationPresent(DeveloperInfo.class)) {
                // Get the annotation instance
                DeveloperInfo info = calculateProgressMethod.getAnnotation(DeveloperInfo.class);

                // Access the elements of the annotation
                System.out.println("\n--- Developer Info for calculateProgress() ---");
                System.out.println("Developer: " + info.developerName());
                System.out.println("Last Modified: " + info.lastModified());
                System.out.println("Description: " + info.description());
            }

            // Get the Method object for formatDate
            Method formatDateMethod = projectUtilsClass.getMethod("formatDate", String.class);

            if (formatDateMethod.isAnnotationPresent(DeveloperInfo.class)) {
                DeveloperInfo info = formatDateMethod.getAnnotation(DeveloperInfo.class);

                System.out.println("\n--- Developer Info for formatDate() ---");
                System.out.println("Developer: " + info.developerName());
                System.out.println("Last Modified: " + info.lastModified());
                System.out.println("Description: " + info.description());
            }

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

මේ code එක run කරාම, calculateProgress() සහ formatDate() methods වලට අපි එකතු කරපු developer information ටික console එකේ print වෙනවා. මේ වගේම frameworks වලටත් පුළුවන් annotations කියවලා, ඒ අනුව code එකේ behavior එක වෙනස් කරන්න. (e.g., Spring finds @Controller classes and registers them).

අවසානයට

ඉතින්, Java Annotations කියන්නේ Java developer කෙනෙකුට නැතුවම බැරි tool එකක්. මේවා code එකට metadata එකතු කරලා, code එකේ readability, maintainability වැඩි කරනවා වගේම, frameworks සහ tools වලටත් අපේ code එක තේරුම් ගන්න උදව් කරනවා. Built-in annotations වගේම, custom annotations හදන එකෙන් අපිට පුළුවන් අපේ applications වලට specific functionalities, validations, සහ configurations add කරන්න. මේවා ගැන හොඳ අවබෝධයක් තියෙන එකෙන් ඔයාට Java වල වඩාත් powerful, scalable, සහ maintainable applications හදන්න පුළුවන් වේවි.

මතක තියාගන්න, හොඳ developer කෙනෙක් කියන්නේ syntax දන්න කෙනෙක් විතරක් නෙවෙයි, තියෙන tools වලින් උපරිම ප්‍රයෝජන ගන්න පුළුවන් කෙනෙක්. මේ annotations ගැන තවදුරටත් හොයලා බලන්න, ඔයාලගේ projects වලට මේවා කොහොමද පාවිච්චි කරන්නේ කියලා බලන්න. ඒ වගේම, ඔයාලට මේ ගැන තියෙන ප්‍රශ්න, අදහස් comment section එකේ දාගෙන යන්න. අපි ඊළඟ article එකෙන් තවත් අලුත් දෙයක් ගැන කතා කරමු! ජයවේවා!