자바 & Groovy & Scala & Kotlin - 33. Reflect 와 주석
이 장 은 주로 실제 프로 그래 밍 에서 매우 중요 한 반사 와 주해 의 두 가지 특성 을 소개 한다.
Java
주해
주 해 는 주로 메타 데 이 터 를 표시 하 는 데 쓰 인 다.자바 에서 주해 사용 기호
@interface
를 설명 합 니 다.주석 만 들 기
예:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Bean {
String name();
}
이상 에서
Bean
라 는 주 해 를 만 들 었 습 니 다.이 주석 에는 사용자 정의 주석 을 표시 하 는 두 개의 JVM 내 장 된 주석 Retention
과 Target
이 사용 되 어 있 습 니 다.Rentention
이 주해 정 보 를 어디 에 저장 해 야 하 는 지 RUNTIME
, CLASS
, SOURCE
세 가지 가 있다 고 밝 혔 다.이 중 CLASS
은 기본 값 으로 RUNTIME
으로 표 시 된 주해 만 반 사 될 수 있다.Target
주 해 를 어디 에 두 어야 하 는 지, TYPE
, FIELD
, METHOD
, PARAMETER
등 몇 가지 가 있다.즉, 성명 TYPE
일 때 주 해 를 클래스 나 인터페이스 에 놓 을 수 있 고, 성명 FIELD
은 방법 속성 에 만 적 용 됩 니 다.위 에서 만 든 주해 에는 하나의 속성
name
이 포함 되 어 있 으 며, 이 주 해 를 사용 할 때 이 속성 을 동시에 정의 해 야 합 니 다.String name() default "";
로 대체 하면 이 속성 을 정의 하지 않 고 기본 값 을 사용 할 수 있 습 니 다.주 해 를 사용 하 다
세 개의 주석 만 들 기
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Bean {
String name();
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BeanField {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeanMethod {
String alias() default "";
}
위 에서 만 든 주석
Bean
을 사용 할 때 name
속성 을 정의 해 야 합 니 다. BeanMethod
의 alias
속성 은 기본 값 의 빈 문자열 이 있 기 때문에 정의 할 때 alias
속성 을 생략 할 수 있 습 니 다.이상 의 주 해 를 사용 하 는 클래스 를 정의 합 니 다.
@Bean(name = "t_person")
class Person {
public Person() {
}
public Person(int age) {
this.age = age;
}
@BeanField
private int age;
@BeanMethod(alias = "trueAge")
public int getAge() {
return age;
}
public void setAge(final int age) {
this.age = age;
}
@BeanMethod(alias = "hello")
public void sayHello(String message) {
System.out.println("hello " + message);
}
}
반사
이른바 반사 란 운행 할 때 클래스 의 각종 메타 데이터 (클래스 자체, 클래스 중의 방법, 클래스 중의 속성 등) 를 얻 는 수단 이다.실제 개발 에서 반 사 는 주로 각종 도 구 를 작성 하 는 데 사용 되 는데, 우 리 는 스스로 반 사 를 작성 해 야 하 는 상황 이 매우 적다.
클래스 참조
하나의 종 류 를 반사 하기 위해 서 는 먼저 클래스 의 정 보 를 얻어 야 한다. 즉, 클래스 의 인용 을 얻 기 위해 자바 에 서 는 다음 과 같은 방법 을 사용 해 야 한다.
Class> clazz = Person.class;
방법의 인용
방법 은 클래스 에 존재 하기 때문에 방법의 인용 을 얻 기 전에 클래스 의 인용 을 먼저 얻어 야 한다.
Method setAgeMethod = clazz.getMethod("setAge", int.class);
이상
getMethod()
방법 을 사용 하여 Person
류 중의 setAge(int)
방법 을 인용 하 였 다.getMethod()
방법의 첫 번 째 매개 변 수 는 방법 명 이 고 그 다음 에 방법 을 나타 내 는 매개 변수 목록 의 변 삼 이다.getMethod()
유형 과 getDeclaredMethod()
라 는 방법 도 있다. 이들 의 가장 현저 한 차 이 는 전 자 는 공개 멤버 만 얻 을 수 있 고 후 자 는 사유 멤버 를 얻 을 수 있다 는 것 이다.기본적으로 방법, 속성, 주해 등의 인용 은 모두 이 두 가지 방법 이 있다.방법의 인용 을 얻 은 후
invoke()
을 통 해 이 방법 을 집행 할 수 있다.invoke()
의 첫 번 째 매개 변 수 는 이 방법 을 실행 해 야 하 는 대상 이 고 그 다음 에 이 방법 에 들 어 오 는 매개 변수 목록 입 니 다.Method setAgeMethod = clazz.getMethod("setAge", int.class);
setAgeMethod.invoke(person, 100);
Method getAgeMethod = clazz.getMethod("getAge");
int age = (int) getAgeMethod.invoke(person);
System.out.println("Age is " + age); // 100
속성 참조
속성의 인용 은 기본적으로 같은 방법의 인용 이다.
Field ageField = clazz.getDeclaredField("age");
속성의 인용 을 통 해 속성의 값 을 얻 으 려 면
getXXX()
같은 방법 으로 만 얻 으 면 됩 니 다. 이 방법 은 매개 변 수 는 이 속성 을 가 진 대상 입 니 다.ageField.setAccessible(true);
System.out.println("Age is " + ageField.getInt(person));
이상
age
은 성명 할 때 개인 변수 이기 때문에 먼저 setAccessible()
를 사용 해 야 접근 할 수 있 습 니 다.구조 방법의 인용
구조 방법의 인용 도 다른 인용 과 차이 가 많 지 않 으 니 예 를 직접 보면 된다.
Constructor constructor = (Constructor) clazz.getConstructor(int.class);
Person person1 = constructor.newInstance(18);
System.out.println("Age is " + person1.getAge());
주해 의 인용
Bean bean = clazz.getAnnotation(Bean.class);
String name = bean.name();
System.out.println("Name is " + name); // t_person
스 트 리밍 멤버
실제 개발 에 서 는 반사 가 주로 도 구 를 만 드 는 데 사용 되 기 때문에 대부분의 경우 반사 되 는 클래스 의 구 조 를 모 르 기 때문에 클래스 의 구성원 을 옮 겨 다 니 며 이상 의 모든 방법 을 결합 해 야 합 니 다.다음은 간단 한 예 다.
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getDeclaredAnnotations();
if (annotations == null || annotations.length == 0) continue;
System.out.println("Find field annotation " + annotations[0].annotationType().getSimpleName());
}
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
Annotation[] annotations = method.getDeclaredAnnotations();
if (annotations == null || annotations.length == 0) continue;
System.out.println("Find method annotation " + annotations[0].annotationType().getSimpleName()); // BeanMethod
BeanMethod beanMethod = (BeanMethod) annotations[0];
String alias = beanMethod.alias();
System.out.println("Alias is " + alias); // trueAge
if (method.getName().equals("sayHello")) {
method.invoke(person, "world");
}
System.out.println("====================");
}
Groovy
주해
Groovy 용법 은 자바 와 같다.
주석 만 들 기
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Bean {
String name()
}
주 해 를 사용 하 다
세 개의 주석 만 들 기
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Bean {
String name()
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
@interface BeanField {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface BeanMethod {
String alias() default ""
}
이상 의 주 해 를 사용 하 는 클래스 를 정의 합 니 다.
@Bean(name = "t_person")
class Person {
@BeanField
def int age
@BeanMethod(alias = "trueAge")
def int getAge() {
age
}
def void setAge(int age) {
this.age = age
}
@BeanMethod(alias = "hello")
def void sayHello(String message) {
println("hello $message")
}
}
반사
Groovy 의 반 사 는 자바 와 같 지만 Groovy 에 서 는 더 강력 한 메타 프로 그래 밍 을 가지 고 있 으 며 나중에 언급 할 것 입 니 다.
클래스 참조
def clazz = Person.class
방법의 인용
def setAgeMethod = clazz.getMethod("setAge", int.class)
이 방법 을 집행 하 다
def setAgeMethod = clazz.getMethod("setAge", int.class)
setAgeMethod.invoke(person, 100)
def getAgeMethod = clazz.getMethod("getAge")
int age = (int) getAgeMethod.invoke(person)
println("Age is " + age) // 100
사용
invoke()
을 제외 하고 Groovy 는 또 다른 방식 으로 실행 하 는 방법 이 있 습 니 다. 사용 할 때 JavaScript 의 eval()
함수 처럼 보 입 니 다.person."${setAgeMethod.name}"(20)
속성 참조
def ageField = clazz.getDeclaredField("age")
속성 값 획득
ageField.setAccessible(true)
println("Age is " + ageField.getInt(person))
구조 방법의 인용
만약 구조 방법의 인용 을 사용한다 면 반드시 필요 한 구조 방법 을 먼저 정의 해 야 하지만, 이렇게 하면 Groovy 구조 방법의 대명 파라미터 의 기능 을 상실 하 게 될 것 이다.
def constructor = clazz.getConstructor(int.class)
def person1 = constructor.newInstance(18)
println("Age is " + person1.getAge())
주해 의 인용
Bean bean = clazz.getAnnotation(Bean.class)
def name = bean.name()
println("name is " + name)
스 트 리밍 멤버
clazz.declaredFields.findAll {
it.declaredAnnotations != null && it.declaredAnnotations.size() > 0
}.each {
println("Find field annotation ${it.annotations[0].annotationType().simpleName}")
}
clazz.declaredMethods.findAll {
it.declaredAnnotations != null && it.declaredAnnotations.size() > 0
}.each {
println("Find method annotation ${it.annotations[0].annotationType().simpleName}")
def alias = (it.annotations[0] as BeanMethod).alias()
println("Alias is $alias")
if (it.name == "sayHello") {
it.invoke(person, "world")
}
println("====================")
}
Scala
주해
Scala 는 대부분 자바 의 주 해 를 직접 사용 하면 됩 니 다.스칼라 자체 도 스칼라 스타일 의 주석 기능 을 제공 하지만 기능 이 약해 자바 로 대체 할 수 있 습 니 다.
주석 만 들 기
Scala 가 주 해 를 만 들 려 면 ClassfileAnnotation 이나 StaticAnnotation 을 계승 해 야 합 니 다.전 자 는 자바 의 Runtime 과 유사 하여 반 사 될 수 있 고 후 자 는 반 사 될 수 없다.
class Bean(val name: String) extends ClassfileAnnotation
주 해 를 사용 하 다
세 개의 주석 만 들 기
class Bean(val name: String) extends ClassfileAnnotation
class BeanField extends StaticAnnotation
class BeanMethod(val alias: String = "") extends StaticAnnotation
이상 의 주 해 를 사용 하 는 클래스 를 정의 합 니 다.
@Bean(name = "t_person")
class Person {
@BeanField
private var privateAge: Int = 0
@BeanMethod(alias = "trueAge")
def age_=(pAge: Int) {
privateAge = pAge
}
def age = privateAge
@BeanMethod
def sayHello(message: String) = println(s"hello $message")
}
반사
Scala 는 자체 반사 Api 가 있어 자바 보다 기능 이 풍부 하지만 개인 적 으로 사용 하기 가 매우 어렵 고 자바 를 직접 사용 하 는 것 이 편리 하 다.스칼라 의 Api 에 관심 이 있 는 사람 은 직접 홈 페이지 에 가서 문 서 를 볼 수 있다.
클래스 에 따라 대상 을 만 드 는 간단 한 스칼라 원생 Api 의 예 를 들 어 얼마나 어 려 운 지 체험 해 보 자.
val classLoaderMirror = runtimeMirror(getClass.getClassLoader)
val typePerson = typeOf[Person]
val classPerson = typePerson.typeSymbol.asClass
val classMirror = classLoaderMirror.reflectClass(classPerson)
val methodSymbol = typePerson.decl(termNames.CONSTRUCTOR).asMethod
val methodMirror = classMirror.reflectConstructor(methodSymbol)
val p: Person = methodMirror(10).asInstanceOf[Person]
p.age = 16
println(p.age)
Kotlin
주해
Kotlin 의 용법 은 자바 와 유사 하지만 큰 차이 가 있다.
주석 만 들 기
AnnotationRetention
자바 와 유사 한 RetentionPolicy
.AnnotationTarget
자바 와 유사 한 ElementType
이지 만 Kotlin 의 특성 상 그 값 은 FIELD
, PROPERTY_GETTER
등 종류 가 있다.@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@Repeatable
@MustBeDocumented
annotation class Bean(val name: String)
주 해 를 사용 하 다
세 개의 주석 만 들 기
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@Repeatable
@MustBeDocumented
annotation class Bean(val name: String)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FIELD)
annotation class BeanField
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.FUNCTION)
annotation class BeanMethod(val alias: String = "")
이상 의 주 해 를 사용 하 는 클래스 를 정의 합 니 다.
@Bean(name = "t_person")
class Person {
@BeanField var age: Int = 0
@BeanMethod(alias = "trueAge") get() = field
@BeanMethod(alias = "hello") fun sayHello(message: String) {
println("hello $message")
}
}
반사
Kotlin 의 반 사 는 기호
::
를 통 해 각 종류 와 구성원 을 직접 인용 할 수 있 지만 문자열 을 통 해 인용 하려 면 매우 번거롭다.클래스 참조
val clazz = Person::class
함수 참조
val sayHello = Person::sayHello
이 함수 실행
println(sayHello.invoke(person, "world"))
클래스 의 함수 처럼 클래스 밖 에 정 의 된 함 수 를 직접 참조 하고 이 인용 을 매개 변수 로 전달 할 수 있 습 니 다.
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))
속성 참조
var name = Person::age
속성 값 획득
name.get(person)
마찬가지 로 클래스 밖의 속성 을 참조 할 수 있다.
var x = 2
println(::x.get())
::x.set(3)
println(x)
구조 방법의 인용
::Person
이 인용 사용
fun factory(f: () -> Person) {
val p = f()
}
factory(::Person)
스 트 리밍 멤버
val bean = clazz.annotations.first {
it.annotationType().typeName == Bean::class.qualifiedName
} as Bean
println("name is ${bean.name}") // t_person
val properties = clazz.declaredMemberProperties
properties.filter {
it.annotations.isNotEmpty()
}.forEach {
println(it.annotations[0].annotationClass.simpleName)
}
val functions = clazz.declaredMemberFunctions
functions.filter {
it.annotations.isNotEmpty()
}.forEach {
println(it.name)
println(it.annotations[0].annotationClass.simpleName) // BeanMethod
val beanMethod = it.annotations[0] as BeanMethod
println("alias is ${beanMethod.alias}") // hello
}
Summary
_33_reflect_annotation
소절
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.