序文
最近、学習プロセス中に問題を発見しました。抽象クラスは、すべての抽象的なメソッドを実装せずに新しいものを介してオブジェクトを構築することはできませんが、抽象的なメソッドは独自の構築方法を持つことができます。これは私を混乱させました。構造方法があり、新しいものを通じて作成できないため、具体的なクラスになっていない場合、抽象クラスをインスタンス化できますか?
Javaでは、抽象クラスを直接インスタンス化することはできません。ただし、抽象クラスのこの機能は、しばしば厄介な障害になります。たとえば、動的プロキシを使用して抽象クラスに抽象的なメソッドを実行する機能を提供する場合、2つの難しさがあります。この標準のJVMには、javassistなどのいくつかの実装があります。これは、bytecodeツールを使用してこれを達成するために使用できます(proxyfactory)。
Androidで抽象クラスオブジェクトを構築する場合は、 new ClassName() {}または継承されている場合のみがあります。ただし、両方の方法をクラスオブジェクトで直接動作させることはできないため、必要な抽象的な機能を達成できない問題につながります。
最初の段落で言及されているシーンの詳細な説明を次に示します。
まず、次のように定義されたインターフェイスファイルがあります(Androidに精通している友人は、これがプロキシオブジェクトを生成するためにレトロフィットに提供されるAPI構成インターフェイスであることを確認できます):
パブリックインターフェイスrealapi {@get( "api1")observable <string> api1(); @get( "api2")observable <string> api2(); @get( "API3")OBSERVABLE <String> API3(); //...他の方法}次に、インターフェイスのメソッドの1つのみを実装する抽象クラスを作成します(インターフェイスデータのシミュレーションに使用):
@mockapippublic abstract class mockapiはrealapiを実装します{observable <string> api3(){return observable.just( "mock data"); }}次に、既存のRealAPIオブジェクトとMockapiクラスを組み合わせて混合オブジェクトを構築するMockManagerなどのツールが必要です。モッカピで既に定義されているメソッドを実行すると、それらを直接実行します。 Mockapiがメソッドを定義しない場合、RealApiメソッドを呼び出します。コールメソッドは大まかです。
RealApi API = mockmanager.build(realapi、mockapi.class);
Javassistを通じて、上記の機能を完了することは非常に簡単です。プロキシファクトリーオブジェクトを作成し、スーパークラスをMockapiに設定し、抽象メソッドをフィルタリングし、メソッドハンドラーを設定して、同じ名前とパラメーターメソッドでRealApiオブジェクトを呼び出します。コードの実装はここでは提供されません。
しかし、Androidでは、Javassistの方法が投げられます
原因:java.lang.unsupportedoperationexception:java.lang.classloader.defineclass(classloader.java:520)でこのタイプのクラスファイルをjava.lang.lang.reflt.method.invoke(ネイティブ方法)でロードすることはできませんjavassist.util.proxy.factoryhelper.toclass2(factoryhelper.java:182)
同様の例外。その理由は、おそらくAndroid上の仮想マシンの実装と標準がわずかに異なるため、ここではダイナミックコード生成アノテーションプロセッサの別の方向に方向を向けます。
注釈プロセッサを使用してそれを実装する場合、アイデアははるかに簡単ですが、プロセスはまだ少し曲がります。
最初に、構築する必要がある抽象クラスをマークするアノテーションを定義します
@target(elementtype.type)@documented@retention(retentionPolicy.source)public @interface mockapi {}プロセッサは、クラスのようなオブジェクトである注釈に基づいて、クラスの要素オブジェクトを取得します。クラスはまだ事前コンパイル段階に存在していないため、 Class.forNameを使用してランタイムに必要なクラスオブジェクトを取得することは不可能です。ただし、要素はクラスリフレクション関連の方法と同様の方法を提供し、TypeLementや実行可能な選挙などの違いもあります。要素オブジェクトを使用して注釈付けされた抽象クラスの抽象的なメソッドは、注釈付き抽象クラスを分析し、クラスを継承する実装クラス(非抽象)を生成し、クラス内のすべての抽象的なメソッドを実装します。これらの抽象的な方法は実際には使用されないため、コンパイルして合格できるはずです。私が選んだ方法は、各メソッド本体が例外をスローすることであり、メソッドは抽象的な方法であり、直接呼び出せないことを促していることです。コードを生成する方法では、いくつかのツールを使用して、リファレンステキストの最後にプロジェクトコードを具体的に実装するオートプロセッサやJavapoetなどの作業を簡素化できます。生成されたコードはほぼこのようなものです。
//生成されたクラス名は、他のクラス名との競合を回避するために、元のクラス名 + "$ empl"の接尾辞に名前が付けられています。この制約は、クラスのパブリックファイナルクラスのmockapi $ emplを反映するためにも使用されます{@override public observable <string> api1(){throw new IllegalStateException( "API1()は抽象的方法です!"); } @Override public Observable <string> api2(){show new IllegalStateException( "API2()は抽象的方法です!"); }}抽象クラスのクラス名に基づいて実装クラスを反映し、そのコンストラクターメソッドを呼び出すことにより、反射に基づいて実装オブジェクトを構築します。
//コードコンストラクトを生成するオブジェクトを取得private static <t> t getimplobject(class <t> cls){try {return(t)class.forname(cls.getName() + "$ inpl")。newInstance(); } catch(例外E){nullを返します。 }}動的プロキシを構築し、それをRealApiの実際のオブジェクトと、前のステップで構築された抽象クラスの実装オブジェクトに渡し、抽象クラスの定義に基づいてどのオブジェクトがそのメソッドの動作をプロにしているかを決定します。それ以外の場合は、インターフェイスの実際のオブジェクトによって実行されます。
public static <origin、mock extends rigin> origin build(final rigin ostirin、final class <mock> mockclass){// mockクラスが閉じているとマークされている場合、(!isenable(mockclass)){return ostion; } final mock mockobject = getimplobject(mockclass); class <?> originclass = ovirin.getClass()。getInterfaces()[0]; return(origin)proxy.newproxyinstance(originclass.getClassloader()、new class [] {originclass}、new ricocationhandler(){@Override public handler(){@override public handler(@override public object invoke(object o、method methods、objects)throws {// mockmethod = mockclass.getdeclaredmethod(method.getname()、method.getparametertypes()); return mockmethod.invoke(mockobject、objects);}}});}上記の作業を完了した後、ビルドメソッドを使用して、最初に述べたように、実際のインターフェイスと抽象クラスメソッドを混合するプロキシオブジェクトを構築できます。呼び出しクラスは本質的にハードコード化されていますが、手動メンテナンスなしで注釈プロセッサによって自動的に生成されます。使用に関しては、基本的にJavassistの実装を使用することと同じです。
この記事に属する方法を使用して、レトロフィットリクエストをシミュレートするツール(記事の最後にリンクがあります)を実装しましたが、本質的には、抽象クラスの構築を必要とする多くのニーズを実装するために使用でき、さらに使用するシナリオを調査する必要があります。
この記事に記載されているソースコードの実装は、プロジェクトRetrofit-Mock-Resultまたはローカルダウンロードに記載されています。
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。