Javaの静的メソッドからgetClass()を呼び出す方法は?


350

静的メソッドが必要なクラスがあります。これらの静的メソッドの中で、メソッドgetClass()を呼び出して次の呼び出しを行う必要があります。

public static void startMusic() {
  URL songPath = getClass().getClassLoader().getResource("background.midi");
}

しかしEclipseは私に言っています:

Cannot make a static reference to the non-static method getClass() 
from the type Object

このコンパイル時エラーを修正する適切な方法は何ですか?


getResource()ユーザー定義のインスタンス(J2SE以外など)のインスタンスが存在する前に使用すると、失敗することがあります。問題は、JREがその段階でブートストラップクラスローダーを使用することです。この場合、(ブートストラップローダーの)クラスパスにアプリケーションリソースがありません。
Andrew Thompson

回答:


616

答え

TheClassName.class代わりに使用してくださいgetClass()

ロガーの宣言

ログ宣言を挿入する簡単な方法を提供するために、これは特定のユースケースで非常に注目を集めているので、私はそれに考えを追加したいと思いました。多くの場合、ログフレームワークは、ログが特定のコンテキスト(完全修飾クラス名など)に制約されることを期待しています。したがって、変更なしでコピーして貼り付けることはできません。貼り付け安全なログ宣言の提案は他の回答で提供されていますが、バイトコードの拡張やランタイムイントロスペクションの追加などの欠点があります。これらはお勧めしません。コピーと貼り付けはエディターの問題なので、エディターソリューションが最適です。

IntelliJでは、ライブテンプレートを追加することをお勧めします。

  • 「log」を略語として使用します
  • private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);テンプレートテキストとして使用します。
  • [変数の編集]をクリックし、式を使用してCLASSを追加します className()
  • ボックスをオンにして、FQ名を再フォーマットして短縮します。
  • コンテキストをJava:宣言に変更します。

入力するlog<tab>と、自動的に展開されます

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);

自動的にインポートを再フォーマットして最適化します。


6
これにより、誤ったコードが別のクラスで実行されている場合、コピー/貼り付けと悲しい悲しい結果になります。
William Entriken

1
@WilliamEntriken:匿名の内部クラスを使用することは、それに対する優れたソリューションではありません。バイトコードを追加のクラスファイルで膨らませて、開発者の数秒の労力を節約します。どこにでもコピー/貼り付けする必要がある場所で人々が何をしているかはわかりませんが、言語のハックではなく、エディタソリューションで扱う必要があります。たとえば、IntelliJを使用している場合は、クラス名を挿入できるライブテンプレートを使用します。
Mark Peters

1
あなたが4つのdownvotesなった理由を私は本当に不思議
アル・Mothafarを

「どこにでもコピー/貼り付けする必要がある場所で人々が何をしているのかわからない」—Androidでの使用などLog、タグの提供が必要なもの(通常はクラス名)。そして、おそらく他の例があります。もちろん、そのためのフィールドを宣言することもできます(これは一般的な方法です)が、それはまだコピーアンドペーストです。
user149408

1
ライブテンプレートソリューションの1つのことを忘れてしまった:[変数の編集]をクリックして、式のでCLASS入力しclassName()ます。これにより、$ CLASS $が実際にクラス名に置き換えられます。そうでない場合は、空になります。
HerbCSO

122

問題のコード例については、標準的な解決策はクラスをその名前で明示的に参照することであり、getClassLoader()呼び出しなしで行うことも可能です。

class MyClass {
  public static void startMusic() {
    URL songPath = MyClass.class.getResource("background.midi");
  }
}

このアプローチには、このコードをいくつかの類似のクラスに複製する必要がある場合に備えて、コピー/貼り付けエラーに対して安全ではないという裏側がまだあります。

そして、見出しの正確な質問については、隣接するスレッドに投稿されたトリックがあります

Class currentClass = new Object() { }.getClass().getEnclosingClass();

ネストされた匿名Objectサブクラスを使用して、実行コンテキストを取得します。このトリックには、コピー/貼り付けが安全であるという利点があります...

他のクラスが継承する基本クラスでこれを使用する場合の注意:

また、このスニペットが基本クラスの静的メソッドとして形成されている場合、currentClass値は常に、そのメソッドを使用している可能性のあるサブクラスではなく、その基本クラスへの参照になります。


23
断然私の好きな答え。NameOfClass.classは明白ですが、クラスの名前を知っている必要があります。getEnclosingClassトリックは、コピーアンドペーストだけでなく、イントロスペクションを利用する静的基本クラスメソッドにも役立ちます
Domingo Ignacio

1
ちなみにとClass.getResource()は少し動作が異なる場合がありますClassLoader.getResource()stackoverflow.com/a/676273を
オーグラ

68

Java7 +では、静的メソッド/フィールドでこれを行うことができます。

MethodHandles.lookup().lookupClass()

mainメソッドからヘルプを印刷するためにgetSimpleName()呼び出しのみが必要な場合の良い解決策。
Dmitrii Bulashevich 2017

6
毎回クラス名を変更せずに、どこにでもロガーを追加するための「コピーペースト」ソリューションを探していました。あなたはそれを私にくれました: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou 2018年

それはサポートされています最適なソリューション、AndroidはAPI 26まで、これをサポートしていないということで欠点、すなわちアンドロイド8
user149408

24

自分でこれに取り組みました。静的なコンテキストでは、現在のスレッドを使用してClassLoaderを取得するのが便利です。これはHadoop MapReduceでも機能します。他のメソッドはローカルで実行されている場合は機能しますが、MapReduceで使用された場合はnull InputStreamを返します。

public static InputStream getResource(String resource) throws Exception {
   ClassLoader cl = Thread.currentThread().getContextClassLoader();
   InputStream is = cl.getResourceAsStream(resource);
   return is;
}


11

getClass() メソッドは、次のシグネチャを持つObjectクラスで定義されています。

public final Class getClass()

として定義されていないためstatic、静的コードブロック内で呼び出すことはできません。詳細については、これらの回答を参照してください:Q1Q2Q3

静的コンテキストの場合、クラスリテラル式を使用してクラスを取得する必要があるため、基本的には次のようにする必要があります。

Foo.class

このタイプの式はクラスリテラルと呼ばれ、Java言語仕様書では次のように説明されています。

クラスリテラルは、クラス、インターフェイス、配列、またはプリミティブ型の名前とそれに続く「。」で構成される式です。とトークンクラス。クラスリテラルのタイプはClassです。現在のインスタンスのクラスの定義クラスローダーによって定義された名前付きの型(またはvoid)のClassオブジェクトに評価されます。

この主題に関する情報は、クラスのAPIドキュメントにもあります。


9

それを試してみてください

Thread.currentThread().getStackTrace()[1].getClassName()

または

Thread.currentThread().getStackTrace()[2].getClassName()

このアプローチの優れた点は、8より前のAndroidバージョン(API 26)でも機能することです。Android 4.4.4では、インデックス[1]によってすべてのログメッセージThreadがソースとして報告されることに注意してください。[2]AndroidとOpenJREの両方で正しい結果が得られるようです。
user149408

0

Utilityクラスがあるとすると、サンプルコードは次のようになります-

    URL url = Utility.class.getClassLoader().getResource("customLocation/".concat("abc.txt"));

CustomLocation-リソース内のフォルダー構造がある場合は、この文字列リテラルを削除します。


-2

このようなものを試してください。わたしにはできる。Logg(クラス名)

    String level= "";

    Properties prop = new Properties();

    InputStream in =
            Logg.class.getResourceAsStream("resources\\config");

    if (in != null) {
        prop.load(in);
    } else {
        throw new FileNotFoundException("property file '" + in + "' not found in the classpath");
    }

    level = prop.getProperty("Level");

1
なぜこのスレッドに既にある同じ答えを3回提供しているのかわからない:2011年から2回、2013
。– Antares42

このソリューションは、「Resources \\ config」フォルダーへのアクセス権を持つクラスローダーによってクラスLoggがロードされる場合に機能します。複数のクラスローダー(例として、libフォルダーをロードするクラスローダーと、アプリのライブラリとリソースをロードするクラスローダー)を持つ一部のサーバーでは、これは正しくありません。そのため、この回答の解決策は偶然にしか機能しない場合があります。
Manuel Romeiro、2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.