ããã¯ããªã«ãããããŠæžãããã®ïŒ
ã¡ã¿ã¢ãããŒã·ã§ã³ã®ã²ãšã€ã«ã@Inherited
ããããŸãã
Inherited (Java SE 21 & JDK 21)
ã¢ãããŒã·ã§ã³ãç¶æ¿ããããšããã®ã§ããããããã©ãããæå³ãªã®ã確èªããããšããªãã£ãã®ã§èŠãŠã¿ãããšã«ããŸããã
@Inheritedã¢ãããŒã·ã§ã³
@Inherited
ã¢ãããŒã·ã§ã³ã®Javadocã®èª¬æãèŠãŠã¿ãŸãã
Inherited (Java SE 21 & JDK 21)
ã¢ãããŒã·ã§ã³ãç¶æ¿ãããããã§ãã
泚éã€ã³ã¿ãã§ãŒã¹ãèªåçã«ç¶æ¿ãããããšã瀺ããŸãã
@Inherited
ã¢ãããŒã·ã§ã³ãä»äžããããšããã¢ãããŒã·ã§ã³ããããããã芪ã¯ã©ã¹ã«ä»äžãããŠããå Žåã¯ãµãã¯ã©ã¹ã«ä»äžãããŠ
ããªããŠã芪ã¯ã©ã¹ã«ä»äžãããŠããã確èªããããã§ãã
ç¶æ¿ãããã¡ã¿æ³šéã泚éã€ã³ã¿ãã§ãŒã¹å®£èšã«ååšãããŠãŒã¶ãŒãã¯ã©ã¹å®£èšã§æ³šéã€ã³ã¿ãã§ãŒã¹ã«ååããè¡ããã¯ã©ã¹å®£èšã«ãã®ã€ã³ã¿ãã§ãŒã¹ã®æ³šéããªãå Žåãã¯ã©ã¹ã»ã¹ãŒããŒã¯ã©ã¹ã«ã¯æ³šéã€ã³ã¿ãã§ãŒã¹ã®ååããèªåçã«è¡ãããŸãã ãã®ããã»ã¹ã¯ããã®ã€ã³ã¿ãã§ãŒã¹ã®æ³šéãèŠã€ããããã¯ã©ã¹éå±€ã®æäžäœ(Object)ã«éãããŸã§ç¹°ãè¿ãããŸãã ãã®ã€ã³ã¿ãã§ãŒã¹ã®æ³šéãæã€ã¹ãŒããŒã¯ã©ã¹ããªãå Žåãååãã§ã¯ã該åœããã¯ã©ã¹ã«ãã®ãããªæ³šéããªãããšã瀺ãããŸãã
å¹æãããã®ã¯ã¯ã©ã¹ã®ã¿ã ããã§ãã
ãã®ã¡ã¿æ³šéä»ãã€ã³ã¿ãã§ãŒã¹ã¯ã泚éä»ãã€ã³ã¿ãã§ãŒã¹ã䜿çšããŠã¯ã©ã¹ä»¥å€ã®æ³šéãä»ããå Žåã¯å¹æããããŸããã
ã€ã³ã¿ãŒãã§ãŒã¹ã«ä»äžããŠãç¡å¹ãªããã§ãã
ãŸãããã®ã¡ã¿æ³šéã®æ©èœã¯ã¹ãŒããŒã»ã¯ã©ã¹ãã泚éãç¶æ¿ãããã ãã§ãããå®è£ ãããã€ã³ã¿ãã§ãŒã¹ã«å¯Ÿãã泚éã¯ç¡å¹ã§ããããšã«ãçæããŠãã ããã
確èªããŠã¿ãŸãããã
ç°å¢
ä»åã®ç°å¢ã¯ãã¡ãã
$ java --version openjdk 21.0.5 2024-10-15 OpenJDK Runtime Environment (build 21.0.5+11-Ubuntu-1ubuntu124.04) OpenJDK 64-Bit Server VM (build 21.0.5+11-Ubuntu-1ubuntu124.04, mixed mode, sharing) $ mvn --version Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.5, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "6.8.0-49-generic", arch: "amd64", family: "unix"
MavenäŸåé¢ä¿ãªã©ã
<properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.11.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.26.3</version> <scope>test</scope> </dependency> </dependencies>
確èªã¯ãã¹ãã³ãŒãã§è¡ããŸãã
確èªå¯Ÿè±¡ã®äœæ
æ¯èŒã®ããã«ã@Inherited
ã¢ãããŒã·ã§ã³ãä»äžããªãã¢ãããŒã·ã§ã³ãš
src/main/java/org/littlewings/annotation/MyAnnotation.java
package org.littlewings.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface MyAnnotation { String value(); }
ä»äžããã¢ãããŒã·ã§ã³ãäœæããŸãã
src/main/java/org/littlewings/annotation/MyInheritableAnnotation.java
package org.littlewings.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited public @interface MyInheritableAnnotation { String value(); }
@Inherited
ã¢ãããŒã·ã§ã³ãä»äžããªãã¢ãããŒã·ã§ã³ã䜿ã£ãã¯ã©ã¹ãã€ã³ã¿ãŒãã§ãŒã¹ã
src/main/java/org/littlewings/annotation/MyAbstractClass.java
package org.littlewings.annotation; @MyAnnotation("super-class") public abstract class MyAbstractClass { @MyAnnotation("super-class-methodA") public void methodA() { } @MyAnnotation("super-class-methodB") public void methodB() { } @MyAnnotation("super-class-abstract-methodA") public abstract void abstractMethodA(); @MyAnnotation("super-class-abstract-methodB") public abstract void abstractMethodB(); }
src/main/java/org/littlewings/annotation/MyInterface.java
package org.littlewings.annotation; @MyAnnotation("interface") public interface MyInterface { @MyAnnotation("interface-methodA") void methodA(); @MyAnnotation("interface-methodB") void methodB(); @MyAnnotation("interface-default-methodA") default void defaultMethodA() { } @MyAnnotation("interface-default-methodB") default void defaultMethodB() { } }
@Inherited
ã¢ãããŒã·ã§ã³ãä»äžããã¢ãããŒã·ã§ã³ã䜿ã£ãã¯ã©ã¹ãã€ã³ã¿ãŒãã§ãŒã¹ã
src/main/java/org/littlewings/annotation/MyAbstractClassWithInherited.java
package org.littlewings.annotation; @MyInheritableAnnotation("super-class") public abstract class MyAbstractClassWithInherited { @MyInheritableAnnotation("super-class-methodA") public void methodA() { } @MyInheritableAnnotation("super-class-methodB") public void methodB() { } @MyInheritableAnnotation("super-class-abstract-methodA") public abstract void abstractMethodA(); @MyInheritableAnnotation("super-class-abstract-methodB") public abstract void abstractMethodB(); }
src/main/java/org/littlewings/annotation/MyInterfaceWithInherited.java
package org.littlewings.annotation; @MyInheritableAnnotation("interface") public interface MyInterfaceWithInherited { @MyInheritableAnnotation("interface-methodA") void methodA(); @MyInheritableAnnotation("interface-methodB") void methodB(); @MyInheritableAnnotation("interface-default-methodA") default void defaultMethodA() { } @MyInheritableAnnotation("interface-default-methodB") default void defaultMethodB() { } }
ãããã䜿ã£ãŠããã¹ãã³ãŒãã§ãªã«ãç¶æ¿ãããŠãªã«ãç¶æ¿ãããªãã®ãã確èªããŠãããŸãã
確èªããŠã¿ã
ã§ã¯ããã¹ãã³ãŒãã§ç¢ºèªããŠãã£ãŠã¿ãŸãããã
@Inheritedã¢ãããŒã·ã§ã³ããªãå Žå
ãŸãã¯@Inherited
ã¢ãããŒã·ã§ã³ããªãå Žåããã
ãã¹ãã³ãŒãã®é圢ã¯ãã¡ãã
src/test/java/org/littlewings/annotation/NonInheritedAnnotationTest.java
package org.littlewings.annotation; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; class NonInheritedAnnotationTest { // ããã«ãã¹ããæžãïŒ }
ã¢ãããŒã·ã§ã³ãçŽæ¥ã¯ã©ã¹ãã¡ãœããã«ä»äžããå Žåã
@Test void direct() throws NoSuchMethodException { @MyAnnotation("class") class MyClass { @MyAnnotation("method") public void method() {} } assertThat(MyClass.class.getAnnotation(MyAnnotation.class).value()) .isEqualTo("class"); assertThat(MyClass.class.getMethod("method").getAnnotation(MyAnnotation.class).value()) .isEqualTo("method"); }
ããã¯ãµã€ãã§ããã
ã¢ãããŒã·ã§ã³ãä»äžããã¯ã©ã¹ã®ç¶æ¿ã
@Test void extendsClass() throws NoSuchMethodException { class MyClass extends MyAbstractClass { @Override public void methodB() { } @Override public void abstractMethodA() { } @MyAnnotation("override-abstract-methodB") @Override public void abstractMethodB() { } } // ã¯ã©ã¹èªäœã«ã¯ä»äžããŠããªãã®ã§null assertThat(MyClass.class.getAnnotation(MyAnnotation.class)) .isNull(); // 芪ã¯ã©ã¹ã®ã¡ãœãããèŠãŠãã assertThat(MyClass.class.getMethod("methodA").getAnnotation(MyAnnotation.class).value()) .isEqualTo("super-class-methodA"); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã§null assertThat(MyClass.class.getMethod("methodB").getAnnotation(MyAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã§null assertThat(MyClass.class.getMethod("abstractMethodA").getAnnotation(MyAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«æ瀺çã«ã¢ãããŒã·ã§ã³ãä»äžããŠãã assertThat(MyClass.class.getMethod("abstractMethodB").getAnnotation(MyAnnotation.class).value()) .isEqualTo("override-abstract-methodB"); }
ã¡ãœããã«é¢ããŠã¯èŠªã¯ã©ã¹ã®ãã®ãèŠãŠãããããããã¯ãªãŒããŒã©ã€ãããã¡ãœããã«ã¢ãããŒã·ã§ã³ãä»äžããŠãããã©ããã§ããã
ã¢ãããŒã·ã§ã³ãä»äžããã€ã³ã¿ãŒãã§ãŒã¹ãå®è£ ã
@Test void implementsInterface() throws NoSuchMethodException { class MyClass implements MyInterface { @Override public void methodA() { } @MyAnnotation("override-interface-methodB") @Override public void methodB() { } @MyAnnotation("override-interface-default-methodB") @Override public void defaultMethodB() { } } // ã¯ã©ã¹ã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã§null assertThat(MyClass.class.getAnnotation(MyAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã§null assertThat(MyClass.class.getMethod("methodA").getAnnotation(MyAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¢ãããŒã·ã§ã³ãä»äžããŠãã assertThat(MyClass.class.getMethod("methodB").getAnnotation(MyAnnotation.class).value()) .isEqualTo("override-interface-methodB"); // ã€ã³ã¿ãŒãã§ãŒã¹ã«å®çŸ©ãããã¡ãœãããèŠãŠãã assertThat(MyClass.class.getMethod("defaultMethodA").getAnnotation(MyAnnotation.class).value()) .isEqualTo("interface-default-methodA"); // ãªãŒããŒã©ã€ãããã¡ãœããã«æ瀺çã«ã¢ãããŒã·ã§ã³ãä»äžããŠãã assertThat(MyClass.class.getMethod("defaultMethodB").getAnnotation(MyAnnotation.class).value()) .isEqualTo("override-interface-default-methodB"); }
ã¯ã©ã¹ãç¶æ¿ããæãšããå€ãããŸããã
@Inheritedã¢ãããŒã·ã§ã³ã䜿ã£ãå Žå
ããã§ã¯@Inherited
ã¢ãããŒã·ã§ã³ã䜿ã£ãå Žåã«ã©ãå€ããã®ããèŠãŠãããŸãããã
ãã¹ãã³ãŒãã®é圢ã¯ãã¡ãã
src/test/java/org/littlewings/annotation/InheritedAnnotationTest.java
package org.littlewings.annotation; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; class InheritedAnnotationTest { // ããã«ããã¹ããæžãïŒ }
çŽæ¥ä»äžããå Žåãç¹ã«æå³ã¯ãªãã§ããã
@Test void direct() throws NoSuchMethodException { @MyInheritableAnnotation("class") class MyClass { @MyInheritableAnnotation("method") public void method() {} } assertThat(MyClass.class.getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("class"); assertThat(MyClass.class.getMethod("method").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("method"); }
ã¢ãããŒã·ã§ã³ãä»äžããã¯ã©ã¹ã®ç¶æ¿ã
@Test void extendsClass() throws NoSuchMethodException { class MyClass extends MyAbstractClassWithInherited { @Override public void methodB() { } @Override public void abstractMethodA() { } @MyInheritableAnnotation("override-abstract-methodB") @Override public void abstractMethodB() { } } // ã¯ã©ã¹èªäœã«ã¯ä»äžããŠããªãã芪ã¯ã©ã¹ã®ã¢ãããŒã·ã§ã³ãåŒãç¶ã assertThat(MyClass.class.getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("super-class"); // 芪ã¯ã©ã¹ã®ã¡ãœãããèŠãŠãã assertThat(MyClass.class.getMethod("methodA").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("super-class-methodA"); // ã¡ãœããã«ã€ããŠã¯åŒãç¶ããªã assertThat(MyClass.class.getMethod("methodB").getAnnotation(MyInheritableAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã§null assertThat(MyClass.class.getMethod("abstractMethodA").getAnnotation(MyInheritableAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«æ瀺çã«ã¢ãããŒã·ã§ã³ãä»äžããŠãã assertThat(MyClass.class.getMethod("abstractMethodB").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("override-abstract-methodB"); }
å
ã»ã©ãšã®éãã¯ããµãã¯ã©ã¹èªäœã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã«ã芪ã¯ã©ã¹ã«ä»äžãããã¢ãããŒã·ã§ã³ãåŒãç¶ãã§ãã
ãšããããšã§ããã
// ã¯ã©ã¹èªäœã«ã¯ä»äžããŠããªãã芪ã¯ã©ã¹ã®ã¢ãããŒã·ã§ã³ãåŒãç¶ã assertThat(MyClass.class.getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("super-class");
äžæ¹ã§ã¡ãœããã«ã€ããŠã¯åŒãç¶ãããŸããã
@Override public void methodB() { } // ã¡ãœããã«ã€ããŠã¯åŒãç¶ããªã assertThat(MyClass.class.getMethod("methodB").getAnnotation(MyInheritableAnnotation.class)) .isNull();
ã¯ã©ã¹ã®ã¿ããšæžãããŠããŸãããããã
ãã®ã¡ã¿æ³šéä»ãã€ã³ã¿ãã§ãŒã¹ã¯ã泚éä»ãã€ã³ã¿ãã§ãŒã¹ã䜿çšããŠã¯ã©ã¹ä»¥å€ã®æ³šéãä»ããå Žåã¯å¹æããããŸããã
ãªããšãªãã¯ã©ã¹ã«ãçŽæ¥ã¢ãããŒã·ã§ã³ãä»äžãããã¿ãŒã³ãæžããŠãããŸããã
@Test void extendsClassDirect() throws NoSuchMethodException { @MyInheritableAnnotation("class") class MyClass extends MyAbstractClassWithInherited { @Override public void methodB() { } @Override public void abstractMethodA() { } @MyInheritableAnnotation("override-abstract-methodB") @Override public void abstractMethodB() { } } // ã¯ã©ã¹èªäœã«ã¯ä»äžããŠãã assertThat(MyClass.class.getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("class"); // 芪ã¯ã©ã¹ã®ã¡ãœãããèŠãŠãã assertThat(MyClass.class.getMethod("methodA").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("super-class-methodA"); // ã¡ãœããã«ã€ããŠã¯åŒãç¶ããªã assertThat(MyClass.class.getMethod("methodB").getAnnotation(MyInheritableAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã§null assertThat(MyClass.class.getMethod("abstractMethodA").getAnnotation(MyInheritableAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«æ瀺çã«ã¢ãããŒã·ã§ã³ãä»äžããŠãã assertThat(MyClass.class.getMethod("abstractMethodB").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("override-abstract-methodB"); }
ã¢ãããŒã·ã§ã³ãä»äžããã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£ ã
@Test void implementsInterface() throws NoSuchMethodException { class MyClass implements MyInterfaceWithInherited { @Override public void methodA() { } @MyInheritableAnnotation("override-interface-methodB") @Override public void methodB() { } @MyInheritableAnnotation("override-interface-default-methodB") @Override public void defaultMethodB() { } } // ã€ã³ã¿ãŒãã§ãŒã¹ã«å®çŸ©ãããã¢ãããŒã·ã§ã³ã¯åŒãç¶ããªã assertThat(MyClass.class.getAnnotation(MyInheritableAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¯ã¢ãããŒã·ã§ã³ãä»äžããŠããªãã®ã§null assertThat(MyClass.class.getMethod("methodA").getAnnotation(MyInheritableAnnotation.class)) .isNull(); // ãªãŒããŒã©ã€ãããã¡ãœããã«ã¢ãããŒã·ã§ã³ãä»äžããŠãã assertThat(MyClass.class.getMethod("methodB").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("override-interface-methodB"); // ã€ã³ã¿ãŒãã§ãŒã¹ã«å®çŸ©ãããã¡ãœãããèŠãŠãã assertThat(MyClass.class.getMethod("defaultMethodA").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("interface-default-methodA"); // ãªãŒããŒã©ã€ãããã¡ãœããã«æ瀺çã«ã¢ãããŒã·ã§ã³ãä»äžããŠãã assertThat(MyClass.class.getMethod("defaultMethodB").getAnnotation(MyInheritableAnnotation.class).value()) .isEqualTo("override-interface-default-methodB"); }
ã¯ã©ã¹ã®ç¶æ¿ã®æãšç°ãªããã€ã³ã¿ãŒãã§ãŒã¹ã«ä»äžãããã¢ãããŒã·ã§ã³ã¯åŒãç¶ããŸããã
// ã€ã³ã¿ãŒãã§ãŒã¹ã«å®çŸ©ãããã¢ãããŒã·ã§ã³ã¯åŒãç¶ããªã assertThat(MyClass.class.getAnnotation(MyInheritableAnnotation.class)) .isNull();
ãããæžãããŠãããšããã§ããã
ãŸãããã®ã¡ã¿æ³šéã®æ©èœã¯ã¹ãŒããŒã»ã¯ã©ã¹ãã泚éãç¶æ¿ãããã ãã§ãããå®è£ ãããã€ã³ã¿ãã§ãŒã¹ã«å¯Ÿãã泚éã¯ç¡å¹ã§ããããšã«ãçæããŠãã ããã
ãšããããã§ã@Inherited
ã¢ãããŒã·ã§ã³ã¯ãããŸã§ã¯ã©ã¹ã®å®çŸ©ã«ä»äžãããã¢ãããŒã·ã§ã³ã«å¯ŸããŠæå¹ãªããšãããããŸããã
ãããã«
@Inherited
ã¢ãããŒã·ã§ã³ã®åäœã«ã€ããŠç°¡åã«ç¢ºèªããŠã¿ãŸããã
åããã¡ãããšæå³ã確èªããŠãããæ¹ãããããªãšæã£ãŠããã®ã§ãå®éã«åããèŠãŠãããŠããã£ãã§ãã
ã¯ã©ã¹ã®å®çŸ©ã«ä»äžãããã¢ãããŒã·ã§ã³ã®ã¿ã察象ããšãããšããããã€ã³ãã§ããã