머리말
최근 학습 과정에서 문제가 발견되었습니다. 초록 클래스는 모든 추상적 인 방법을 구현하지 않고는 새로운 객체를 구성 할 수 없지만 추상 방법에는 고유 한 구성 방법이있을 수 있습니다. 이것은 나를 혼란스럽게했다. 시공 방법이 있고 새로운 것을 통해 만들 수 없기 때문에 구체적인 클래스가되지 않았을 때 추상 클래스를 인스턴스화 할 수 있습니까?
Java에서는 추상 클래스를 직접 인스턴스화 할 수 없습니다. 그러나 추상 클래스 의이 특징은 종종 번거로운 장애물이됩니다. 예를 들어, 동적 프록시를 사용하여 추상 클래스에 초록 메소드를 실행할 수있는 기능을 제공하려면 두 가지 어려움이 있습니다. 1. 동적 프록시는 인터페이스를 구현하는 프록시 객체 만 만들 수 있지만 추상 클래스를 상속하는 객체는 아닙니다. 이 표준 JVM의 경우 Javassist와 같은 일부 구현이 있습니다.이 구현은 Bytecode 도구 (proxyfactory)를 사용하여이를 수행하는 데 사용할 수 있습니다.
Android에서 초록 클래스 객체를 구성하려면 new ClassName() {} 또는 상속 된 경우 만있을 수 있습니다. 그러나 두 방법 모두 클래스 객체에 의해 직접 작동 할 수 없으므로 필요한 추상 기능을 달성 할 수없는 몇 가지 문제가 발생합니다.
다음은 첫 번째 단락에서 언급 된 장면에 대한 자세한 설명입니다.
먼저, 다음과 같이 정의 된 인터페이스 파일이 있습니다 (Android에 익숙한 친구들은 이것이 프록시 객체를 생성하기 위해 로트로 제공되는 API 구성 인터페이스임을 알 수 있습니다).
public interface realapi {@get ( "api1") 관찰 가능 <string> api1 (); @Get ( "API2") Observable <string> api2 (); @Get ( "API3") Observable <string> api3 (); //... 다른 방법}다음으로, 초록 클래스를 작성하여 인터페이스의 메소드 중 하나만 구현하십시오 (인터페이스 데이터를 시뮬레이션하는 데 사용).
@MockApiPublic Abstract Class Mockapi는 Realapi {Observable <string> api3 () {return observable.just ( "mock data"); }}그런 다음 기존 realapi 객체와 Mockapi 클래스를 결합하여 혼합 된 객체를 구성하는 Mockmanager와 같은 도구가 필요합니다. Mockapi에서 이미 정의 된 방법을 실행할 때 직접 실행됩니다. Mockapi가 메소드를 정의하지 않으면 Realapi 메소드를 호출합니다. 호출 방법은 대략입니다.
realapi api = mockmanager.build (realapi, mockapi.class);
Javassist를 통해 위의 기능을 완료하는 것은 매우 간단합니다. 근접한 객체를 만들고 슈퍼 클래스를 Mockapi로 설정 한 다음 추상 메소드를 필터링 한 다음 메소드 핸들러를 동일한 이름과 매개 변수 메소드로 RealAPI 객체를 호출하도록 설정하십시오. 코드 구현은 여기에 제공되지 않습니다.
그러나 Android에서는 Javassist의 방법이 던져 질 것입니다.
원인 : java.lang.unsupportedoperationException : java.lang.classloader.defineclass (classloader.java:520) 에서이 유형의 클래스 파일을로드 할 수 없습니다. javassist.util.proxy.factoryHelper.toclass2 (팩토리 헬퍼 .java:182)
비슷한 예외. 그 이유는 아마도 안드로이드에서 가상 머신의 구현과 표준이 약간 다르기 때문에 여기서는 방향을 동적 코드 생성 주석 프로세서의 다른 방향으로 바꿉니다.
주석 프로세서를 사용하여 구현하는 경우 아이디어가 훨씬 간단하지만 프로세스는 여전히 약간 구부러집니다.
먼저 구성되어야하는 추상 클래스를 표시하기 위해 주석을 정의합니다.
@TARGET (ElementType.type)@documented@rendent (rendentionpolicy.source) public @interface mockapi {} 프로세서는 클래스와 같은 객체 인 주석을 기반으로 클래스의 요소 객체를 얻습니다. 클래스는 사전 컴파일 단계에 아직 존재하지 않기 때문에 Class.forName 사용하여 런타임에 필요한 클래스 객체를 얻는 것은 불가능합니다. 그러나 요소는 클래스 반사 관련과 유사한 방법을 제공하며 타이핑 및 ExecutableElement와 같은 차이점도 있습니다. 요소 객체를 사용하여 주석이 달린 초록 클래스의 추상적 인 방법은 주석이 달린 초록 클래스를 분석하고, 클래스를 물려받는 구현 클래스 (비 임계)를 생성하고 클래스에서 모든 추상 방법을 구현합니다. 이러한 추상적 인 방법은 실제로 사용되지 않기 때문에 컴파일하고 통과 할 수 있어야합니다. 내가 선택한 방식은 각 방법 본체가 예외를 던지는 것입니다.이 방법은 추상적 인 방법이며 직접 호출 할 수 없다는 것을 알 수 있습니다. 코드를 생성하는 방법은 일부 도구를 사용하여 자동 프로세서 및 Javapoet과 같은 작업을 단순화 할 수 있으며, 이는 참조 텍스트 끝에서 프로젝트 코드를 구체적으로 구현합니다. 생성 된 코드는 다음과 같습니다.
// 생성 된 클래스 이름은 다른 클래스 이름과의 충돌을 피하기 위해 원래 클래스 이름 + "$ emp"의 접미사로 명명되었습니다. 이 제약 조건은 또한 클래스 공개 최종 클래스 Mockapi $ impl을 반영하는 데 사용됩니다. mockapi {@override public verientable <string> api1 () {throw new new OrgalStateException ( "API1 ())은 추상 방법입니다!"); } @override public veriversable <string> api2 () {throw new neveralstateException ( "api2 ()는 추상 방법입니다!"); }}추상 클래스의 클래스 이름을 기반으로 구현 클래스를 반영한 다음 생성자 메소드를 호출하여 반사를 기반으로 구현 객체를 구성하십시오.
// 코드 구성을 생성하는 객체를 가져옵니다. private static <t> t getImplObject (class <t> cls) {try {return (t) class.forname (cls.getName () + "$ emp"). newInstance (); } catch (예외 e) {return null; }}동적 프록시를 구성하고, realapi의 실제 객체와 이전 단계에서 구성된 추상 클래스의 구현 객체를 전달하고, 추상 클래스의 정의를 기반으로 메소드 동작을 프록시하는지 결정하십시오. 추상 클래스에 정의가있는 경우, 방법은 초록 클래스의 구현 객체가 실행됩니다. 그렇지 않으면 인터페이스의 실제 객체에 의해 실행됩니다.
public static <origin, mock은 원산지> 원산지 빌드 (최종 원점 원점, 최종 클래스 <mock> mockclass) {// mock 클래스가 닫히는 것으로 표시되면 (! isenable (mockclass)) {return origin; } 최종 모의 mockObject = getImplObject (mockclass); class <?> originclass = origin.getClass (). getInterfaces () [0]; return (origin) proxy.newproxyInstance (originclass.getClassLoader (), new class [] {originclass}, new invocationHandler () {@override public object invoke (개체 O, 메소드 메소드, 오브젝트 [] 객체) 던질 가능 {// 정의 된 추상 분류에서 동일한 이름의 방법을 얻는지 여부를 결정합니다. mockclass.getDeclaredMethod (method.getName (), method.getParameterTypes ())} {} if (mockMethod == null || modifier.isabstract (mockmethod.getModifiers)) {returns (Origins) mockmethod.invoke (mockobject, objects);});}.위의 작업을 완료 한 후 빌드 방법을 사용하여 처음에 언급 된대로 실제 인터페이스와 추상 클래스 메소드를 혼합하는 프록시 객체를 구성 할 수 있습니다. 호출 클래스는 본질적으로 하드 코딩되지만 수동 유지 보수없이 주석 프로세서에 의해 자동으로 생성됩니다. 사용 측면에서 기본적으로 Javassist 구현을 사용하는 것과 동일합니다.
이 기사에 속한 방법을 사용하여 개조 요청을 시뮬레이션하는 도구 (기사 끝에 링크가 있음)를 구현했지만 본질적으로 초록 클래스의 구성이 필요한 많은 요구를 구현하는 데 사용될 수 있으며 더 많은 사용 시나리오를 여전히 탐색해야합니다.
이 기사에 언급 된 소스 코드 구현은 프로젝트 Retrofit-Mock-Result 또는 로컬 다운로드에서 찾을 수 있습니다.
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.