Java'da statik bir yöntemden getClass () nasıl çağırılır?


350

Bazı statik yöntemleri olması gereken bir sınıf var. Bu statik yöntemlerin içinde aşağıdaki çağrı yapmak için getClass () yöntemini çağırmak gerekir:

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

Ancak Eclipse bana şunları söylüyor:

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

Bu derleme zamanı hatasını düzeltmenin uygun yolu nedir?


getResource()Önceden kullanıldığında , kullanıcı tanımlı (örn. J2SE olmayan) bir sınıf örneği bazen başarısız olur. Sorun, JRE'nin sınıf yolunda (bootstrap yükleyicisinin) uygulama kaynaklarına sahip olmayacak şekilde bootstrap sınıf yükleyicisini kullanmasıdır.
Andrew Thompson

Yanıtlar:


617

Cevap

Bunun TheClassName.classyerine kullanın getClass().

Kaydedicilerin Bildirilmesi

Bu, belirli bir usecase için çok fazla dikkat çektiğinden - günlük bildirimleri eklemek için kolay bir yol sağlamak için - düşüncelerimi buna ekleyeceğimi düşündüm. Günlük çerçeveleri genellikle tam nitelikli bir sınıf adı gibi günlüklerin belirli bir bağlamla kısıtlanmasını bekler. Bu yüzden değişiklik yapılmadan kopyalanamazlar. Yapıştırma güvenli günlük bildirimleri için öneriler diğer yanıtlarda sağlanır, ancak bayt kodunu şişirmek veya çalışma zamanı içgözlemini eklemek gibi olumsuz yönleri vardır. Bunları tavsiye etmiyorum. Kopyala-yapıştır bir editör sorunudur, bu yüzden bir editör çözümü en uygunudur.

IntelliJ'de bir Canlı Şablon eklemenizi öneririm:

  • Kısaltma olarak "log" kullanın
  • private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);Şablon metni olarak kullanın .
  • Değişkenleri Düzenle'yi tıklayın ve ifadeyi kullanarak CLASS ekleyin className()
  • FQ adlarını yeniden biçimlendirmek ve kısaltmak için kutuları işaretleyin.
  • Bağlamı Java: bildirimi olarak değiştirin.

Şimdi yazarsanız, log<tab>otomatik olarak

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

Ve içe aktarmaları sizin için otomatik olarak yeniden biçimlendirin ve optimize edin.


6
Bu, yanlış kod farklı bir sınıfta çalışırken kopyalama / yapıştırma ve üzücü üzücü sonuçlarla sonuçlanır.
William Entriken

1
@WilliamEntriken: Anonim iç sınıfları kullanmak bunun için mükemmel bir çözüm değil, sadece birkaç saniye geliştirici çabasını kurtarmak için bayt kodunuzu ekstra sınıf dosyalarıyla şişiriyor. İnsanların her yere kopyalamak / yapıştırmak zorunda oldukları yerde ne yaptıklarından emin değilim, ancak bir dil kesmekle değil, bir editör çözümüyle tedavi edilmelidir. örneğin IntelliJ kullanıyorsanız, sınıf adını ekleyebilen bir Canlı Şablon kullanın.
Mark Peters

“İnsanların her yere kopyalamak / yapıştırmak zorunda oldukları yerde ne yaptıklarından emin değilim” Log- Android'de kullanmak gibi bir etiket sağlanmasını gerektiren bir şey, genellikle sınıf adı. Ve muhtemelen başka örnekler de var. Tabii ki bunun için bir alan ilan edebilirsiniz (ki bu yaygın bir uygulamadır), ancak yine de kopyala yapıştır.
user149408

@ user149408: Evet bu çok konuşuldu. Bu usecase'in orijinal soru ile gerçekten ilgisi olmamasına rağmen, insanların bu soruyu ziyaret etmelerinin yaygın bir nedeni olduğundan cevaba bazı düşünceler ekledim.
Mark Peters

1
Eğer Canlı Şablon çözeltide bir şeyi unuttu: "Edit değişkenleri" tıklayın için Expression CLASStip className(). Bu, $ CLASS $ 'ın aslında sınıf adıyla değiştirildiğinden emin olur, aksi takdirde boş kalır.
HerbCSO

122

Sorudaki kod örneğine gelince, standart çözüm sınıfa açıkça adıyla başvurmaktır ve getClassLoader()çağrı yapmadan da yapmak mümkündür :

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

Bu yaklaşımın, yine de bu kodu benzer sınıflara çoğaltmanız gerektiğinde kopyala / yapıştır hatalarına karşı çok güvenli olmadığı bir arka tarafı vardır.

Başlıktaki kesin soruya gelince, bitişik evrede bir numara var :

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

ObjectYürütme bağlamını elde etmek için iç içe anonim bir alt sınıf kullanır . Bu hile, kopyala / yapıştır güvenli olma avantajına sahiptir ...

Bunu diğer sınıfların miras aldığı Temel Sınıfta kullanırken dikkat edin:

Bu pasajın bazı temel sınıfların statik bir yöntemi olarak şekillendirilmesi durumunda, currentClassdeğerin, bu yöntemi kullanan herhangi bir alt sınıfa değil, her zaman bu temel sınıfa referans olacağını belirtmek gerekir.


23
Şimdiye kadar en sevdiğim cevap. NameOfClass.class açıktır, ancak bu sınıfınızın adını bilmenizi gerektirir. GetEnclosingClass hile sadece kopya yapıştırma için değil, aynı zamanda içgözlemden yararlanan statik temel sınıf yöntemleri için de yararlıdır
Domingo Ignacio

1
Bu arada Class.getResource(), biraz farklı davranabilir ClassLoader.getResource(); bkz. stackoverflow.com/a/676273
augurar

69

Java7 + 'da bunu statik yöntemlerde / alanlarda yapabilirsiniz:

MethodHandles.lookup().lookupClass()

Sadece ana yöntemden yardım yazdırmak için getSimpleName () çağrısına ihtiyacınız varsa güzel bir çözüm .
Dmitrii Bulashevich

6
Sınıf adını her seferinde değiştirmeden, her yerde logger eklemek için bir 'kopyala yapıştır' çözümü arıyordum. Bana sen verdin:private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou

Desteklendiği en iyi çözüm, dezavantajı, Android'in API 26'ya kadar, yani Android 8'e kadar bunu desteklememesidir.
user149408

24

Bununla kendim güreştim. Güzel bir hile, statik bir bağlamda bir ClassLoader almak için geçerli iş parçacığı kullanmaktır. Bu bir Hadoop MapReduce'da da çalışacaktır. Diğer yöntemler yerel olarak çalışırken işe yarar, ancak MapReduce içinde kullanıldığında boş bir InputStream döndürür.

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


11

getClass() yöntemi Object sınıfında aşağıdaki imzayla tanımlanır:

genel final Sınıfı getClass ()

Olarak tanımlanmadığından static, statik bir kod bloğu içinde çağıramazsınız. Daha fazla bilgi için bu cevapları görün Q1 , Q2 , Q3 .

Statik bir bağlamdaysanız, Sınıfı almak için sınıf değişmez ifadesini kullanmanız gerekir, bu nedenle temel olarak aşağıdakileri yapmanız gerekir:

Foo.class

Bu ifade türüne Sınıf Değişmezleri denir ve Java Dil Belirtme Kitabında aşağıdaki gibi açıklanır :

Sınıf değişmezi, bir sınıf, arabirim, dizi veya ilkel türün adını ve ardından `` '' ifadesini içeren bir ifadedir. ve token sınıfı. Sınıf değişmezinin türü Sınıf'tır. Geçerli örnek sınıfının tanımlayıcı sınıf yükleyicisi tarafından tanımlanan, adlandırılan tür (veya boşluk için) için Class nesnesini değerlendirir.

Bu konuyla ilgili bilgileri Class API belgelerinde de bulabilirsiniz .


9

Dene

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

Veya

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

Bu yaklaşımın güzelliği, 8'den önceki Android sürümlerinde de çalışacak olmasıdır (API 26). Dizinin [1], tüm günlük mesajlarının ThreadAndroid 4.4.4'te kaynağı olarak raporlanmasına neden olacağını unutmayın . [2]hem Android hem de OpenJRE'de doğru sonucu veriyor gibi görünüyor.
user149408

0

Bir Utility sınıfı olduğunu varsayalım, o zaman örnek kod -

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

CustomLocation - kaynaklar içindeki herhangi bir klasör yapısı aksi takdirde bu dize hazır bilgisini kaldırır.


-2

Böyle bir şey deneyin. Benim için çalışıyor. Kayıt (Sınıf adı)

    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
Zaten bu konuda üç kez aynı cevabı verdiğinizden emin değilim: 2011'den iki kez, 2013'ten bir kez.
Antares42

Bu çözüm, Logg sınıfı "resources \\ config" klasörüne erişimi olan classloader tarafından yüklendiğinde çalışır. Birden fazla sınıf yükleyicisi olan bazı sunucularda (örneğin, lib klasörünü yüklemek için bir sınıf yükleyici ve uygulama kütüphanelerini ve kaynaklarını yüklemek için başka bir sınıf yükleyici), bu doğru değildir. Bu nedenle, bu cevaptaki çözüm poupose sadece bazen tesadüfle çalışır!
Manuel Romeiro
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.