Bagaimana cara memanggil getClass () dari metode statis di Java?

350

Saya memiliki kelas yang harus memiliki beberapa metode statis. Di dalam metode statis ini saya perlu memanggil metode getClass () untuk melakukan panggilan berikut:

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

Namun Eclipse memberi tahu saya:

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

Apa cara yang tepat untuk memperbaiki kesalahan waktu kompilasi ini?

Rama
sumber
Menggunakan getResource()sebelum ada turunan dari kelas yang ditentukan pengguna (misalnya non-J2SE) terkadang akan gagal. Masalahnya adalah bahwa JRE akan menggunakan bootstrap class-loader pada tahap itu, yang tidak akan memiliki sumber daya aplikasi di jalur-kelas (dari bootstrap loader).
Andrew Thompson

Jawaban:

616

Jawabannya

Cukup gunakan TheClassName.classsaja getClass().

Deklarasikan Penebang

Karena ini mendapat begitu banyak perhatian untuk penggunaan khusus - untuk memberikan cara mudah untuk memasukkan deklarasi log - saya pikir saya akan menambahkan pemikiran saya tentang itu. Kerangka kerja log sering mengharapkan log dibatasi pada konteks tertentu, misalnya nama kelas yang sepenuhnya memenuhi syarat. Jadi mereka tidak dapat disalin tanpa modifikasi. Saran untuk deklarasi log safe-paste disediakan dalam jawaban lain, tetapi mereka memiliki kelemahan seperti menggembungkan bytecode atau menambahkan introspeksi runtime. Saya tidak merekomendasikan ini. Copy-paste adalah masalah editor , jadi solusi editor yang paling tepat.

Di IntelliJ, saya sarankan menambahkan Template Langsung:

  • Gunakan "log" sebagai singkatan
  • Gunakan private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);sebagai teks templat.
  • Klik Edit Variabel dan tambahkan CLASS menggunakan ekspresi className()
  • Centang kotak untuk memformat ulang dan mempersingkat nama FQ.
  • Ubah konteks menjadi Java: deklarasi.

Sekarang jika Anda mengetiknya log<tab>akan secara otomatis diperluas ke

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

Dan secara otomatis memformat ulang dan mengoptimalkan impor untuk Anda.

Mark Peters
sumber
6
Ini akan menghasilkan hasil copy / paste dan sedih sedih ketika kode yang salah berjalan di kelas yang berbeda.
William Entriken
1
@ WilliamEntriken: Menggunakan kelas dalam anonim bukan solusi yang bagus untuk itu, itu hanya membengkokkan bytecode Anda dengan classfiles ekstra untuk menghemat beberapa detik upaya pengembang. Saya tidak yakin apa yang dilakukan orang di mana mereka harus menyalin / menempel di semua tempat, tetapi harus diperlakukan dengan solusi editor, bukan hack bahasa. mis. jika menggunakan IntelliJ, gunakan Live Template yang dapat memasukkan nama kelas.
Mark Peters
1
Saya benar-benar bertanya-tanya mengapa Anda mendapat 4 downvotes
Al-Mothafar
"Saya tidak yakin apa yang dilakukan orang di mana mereka harus menyalin / menempel di semua tempat" - sesuatu seperti menggunakan Logdi Android, yang membutuhkan tag yang harus disediakan, biasanya nama kelas. Dan mungkin ada contoh lain. Tentu saja Anda dapat mendeklarasikan bidang untuk itu (yang merupakan praktik umum), tetapi itu masih menyalin dan menempel.
user149408
1
Anda lupa satu hal dalam solusi Templat Langsung Anda: Klik "Edit variabel" dan dalam Ekspresi untuk CLASSmengetik className(). Itu akan memastikan bahwa $ CLASS $ benar-benar diganti dengan nama kelas, kalau tidak hanya akan keluar kosong.
HerbCSO
122

Adapun contoh kode dalam pertanyaan, solusi standar adalah merujuk kelas secara eksplisit dengan namanya, dan bahkan mungkin dilakukan tanpa getClassLoader()panggilan:

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

Pendekatan ini masih memiliki sisi belakang yang tidak terlalu aman terhadap kesalahan salin / rekat jika Anda perlu mereplikasi kode ini ke sejumlah kelas serupa.

Dan untuk pertanyaan yang tepat dalam informasi utama, ada trik yang diposting di utas yang berdekatan :

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

Ini menggunakan anonim bersarang Object subclass untuk mendapatkan konteks eksekusi. Trik ini bermanfaat untuk menjadi salin / tempel aman ...

Perhatian saat menggunakan ini di Kelas Dasar yang berasal dari kelas lain:

Perlu juga dicatat bahwa jika potongan ini dibentuk sebagai metode statis dari beberapa kelas dasar maka currentClassnilai akan selalu menjadi referensi ke kelas dasar itu daripada ke subkelas yang mungkin menggunakan metode itu.

Sergey Ushakov
sumber
23
Sejauh ini jawaban favorit saya. NameOfClass.class sudah jelas, tetapi itu mengharuskan Anda untuk mengetahui nama kelas Anda. Trik getEnclosingClass tidak hanya berguna untuk copy-paste, juga berguna untuk metode kelas dasar statis yang memanfaatkan introspeksi
Domingo Ignacio
1
Kebetulan Class.getResource()mungkin berperilaku sedikit berbeda dari ClassLoader.getResource(); lihat stackoverflow.com/a/676273
augurar
68

Di Java7 + Anda dapat melakukan ini dalam metode / bidang statis:

MethodHandles.lookup().lookupClass()
Kendali
sumber
Solusi yang bagus jika Anda hanya perlu getSimpleName () panggilan untuk mencetak bantuan dari metode utama .
Dmitrii Bulashevich
6
Saya mencari solusi 'copy paste' untuk menambahkan logger di mana saja, tanpa mengubah nama kelas setiap kali. Anda memberikannya kepada saya: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou
Solusi terbaik di mana didukung, kelemahannya adalah bahwa Android tidak mendukung ini sampai API 26, yaitu Android 8.
user149408
24

Saya sendiri bergulat dengan ini. Trik yang bagus adalah menggunakan gunakan utas saat ini untuk mendapatkan ClassLoader ketika dalam konteks statis. Ini akan bekerja di Hadoop MapReduce juga. Metode lain berfungsi saat berjalan secara lokal, tetapi mengembalikan InputStream nol saat digunakan di MapReduce.

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

Cukup gunakan literal kelas, yaitu NameOfClass.class

Michael Borgwardt
sumber
11

getClass() Metode didefinisikan dalam kelas Obyek dengan tanda tangan berikut:

getClass akhir kelas publik ()

Karena tidak didefinisikan sebagai static, Anda tidak dapat menyebutnya dalam blok kode statis. Lihat jawaban ini untuk informasi lebih lanjut: Q1 , Q2 , Q3 .

Jika Anda berada dalam konteks statis, maka Anda harus menggunakan ekspresi literal kelas untuk mendapatkan Kelas, jadi pada dasarnya Anda harus melakukan seperti:

Kelas

Jenis ekspresi ini disebut Literals Kelas dan dijelaskan dalam Buku Spesifikasi Bahasa Jawa sebagai berikut:

Literal kelas adalah ekspresi yang terdiri dari nama kelas, antarmuka, array, atau tipe primitif diikuti oleh `. ' dan kelas token. Jenis literal kelas adalah Kelas. Ini mengevaluasi ke objek Kelas untuk jenis bernama (atau untuk batal) seperti yang didefinisikan oleh pemuat kelas mendefinisikan kelas instance saat ini.

Anda juga dapat menemukan informasi tentang subjek ini pada dokumentasi API untuk Kelas.

melihcelik
sumber
9

Cobalah

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

Atau

Thread.currentThread().getStackTrace()[2].getClassName()
Ishfaq
sumber
Keindahan dari pendekatan ini adalah bahwa itu juga akan bekerja pada versi Android sebelum 8 (API 26). Berhati-hatilah bahwa indeks [1]akan menyebabkan semua pesan log dilaporkan Threadsebagai sumbernya di Android 4.4.4. [2]tampaknya memberikan hasil yang benar pada Android dan OpenJRE.
user149408
0

Misalkan ada kelas Utilitas, maka kode sampel akan menjadi -

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

CustomLocation - jika ada struktur folder dalam sumber daya yang lain hilangkan string literal ini.

Pravind Kumar
sumber
-2

Coba sesuatu seperti ini. Ini bekerja untuk saya. Logg (Nama kelas)

    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");
Oliver Lagerbäck Westblom
sumber
1
Tidak yakin mengapa Anda memberikan jawaban yang sama yang sudah ada di utas ini tiga kali: Dua kali dari 2011, sekali dari 2013.
Antares42
Solusi ini berfungsi jika kelas Logg dimuat oleh classloader dengan akses ke folder "resources \\ config". Pada beberapa server yang memiliki beberapa classloader (dengan contoh satu classloader memuat folder lib dan lainnya untuk memuat lib dan sumber daya aplikasi), itu tidak benar. Untuk alasan itu, solusi poupose pada jawaban ini hanya berfungsi kadang-kadang secara kebetulan!
Manuel Romeiro