Genel bir sınıfta statik yöntem?


197

Java'da şöyle bir şey istiyorum:

class Clazz<T> {
  static void doIt(T object) {
    // ...
  }
}

Ama anladım

Statik olmayan T tipine statik referans yapılamıyor

Temel kullanımların ötesinde jenerik ilaçları anlamıyorum ve bu yüzden bunu anlamıyorum. Konu hakkında internette fazla bilgi bulamadım.

Birisi böyle bir kullanımın mümkün olup olmadığını benzer şekilde açıklayabilir mi? Ayrıca, ilk denemem neden başarısız oldu?

Yanıtlar:


271

Statik yöntemlerde veya statik alanlarda bir sınıfın genel tür parametrelerini kullanamazsınız. Sınıfın tür parametreleri yalnızca örnek yöntemleri ve örnek alanları için geçerlidir. Statik alanlar ve statik yöntemler için, sınıfın tüm örnekleri arasında, hatta farklı tip parametrelerinin örnekleri arasında paylaşılırlar, bu yüzden belli bir tip parametresine bağlı olamazlar.

Sorununuzun sınıfın type parametresini kullanması gerektiği gibi görünmüyor. Yapmaya çalıştığınız şeyi daha ayrıntılı olarak açıklarsanız, belki daha iyi bir yol bulmanıza yardımcı olabiliriz.


9
Bu cevap, aslında bir çözüm sağlamak yerine posterin problemini açıklıyor.
Jorn

7
@Andre: Sezginiz asılsız değil; C # gerçekten jeneriklere bu şekilde davranır.
jyoungdev

33
"Statik alanlar ve statik yöntemler için, sınıfın tüm örnekleri arasında, hatta farklı tip parametrelerinin örnekleri arasında paylaşılırlar ..." Hata! Tekrar silme tipine göre fındıklara tekme attı!
BD, Rivenhill'de

2
genel sınıf / yöntemlerin derlemeden sonra nasıl göründüğüne bakarsanız, genel özniteliğin kaldırıldığını görürsünüz. Ve sonra derleme <Integer> "Liste" gibi görünüyor. Yani derlemeden sonra List <Integer> ve List <Long> arasında bir fark yok - her ikisi de List oldu.
Dainius

3
Bence ne yapmaya çalıştığını gayet iyi açıkladı. Bu dat ganimet sallamak için çalışıyor açıktır! hah
astryk

139

Java, Tsiz bir tür başlatana kadar ne olduğunu bilmiyor .

Belki arayarak statik yöntemler yürütebilirsiniz Clazz<T>.doit(something)ama bu yapamaz gibi geliyor.

İşleri halletmenin diğer yolu, type parametresini yöntemin kendisine koymaktır:

static <U> void doIt(U object)

hangi U doğru kısıtlama almaz, ama hiçbir şey daha iyidir ....


Sonra herhangi bir kısıtlama, yani Clazz <Object> .doIt (nesne) yerine Clazz.doIt (nesne) belirtmeden yöntemi çağırır, değil mi? Bunu iyi mi düşünüyorsun?
André Chalella

İkinci sözdizimi daha doğru olanıdır, eğer derleyici yöntemin çağrıldığı contaxt'den dönüş değeri türünü çıkartamazsa gerekir. Başka bir deyişle, derleyici Clazz.doIt (object) öğesine izin veriyorsa, bunu yapın.
skaffman

1
Clazz <Object> .doIt (nesne) denedim ve derleme zamanı hatası var! Msgstr "Belirteç (ler) de sözdizimi hatası, yanlış yerleştirilmiş yapılar". Clazz.doIt (nesne) bir uyarı bile olsa iyi çalışıyor.
André Chalella

8
Clazz kullanın. <Object> doIt (nesne). Sanırım C ++ 's Clazz <int> ::
doIt

Neden size U üzerinde doğru kısıtlama getirmediğini söylüyorsunuz?
Adam Burley

46

Ben de aynı problemle karşılaştım. Collections.sortJava çerçevesindeki kaynak kodunu indirerek cevabımı buldum . Kullandığım cevap <T>jenerikleri sınıf tanımına değil yönteme koymaktı .

Bu işe yaradı:

public class QuickSortArray  {
    public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){
//do it
}

}

Tabii ki, yukarıdaki cevapları okuduktan sonra bunun genel bir sınıf kullanmadan kabul edilebilir bir alternatif olacağını fark ettim:

public static void quickSort(Comparable[] array, int bottom, int top){
//do it
}

8
Kabul edilen cevap teknik olarak doğru olsa da: Google beni bu soruya yönlendirdiğinde aradığım şey buydu.
Jeremy List

T extends XXXSözdizimini içeren bir örnek sağlama desteği .
Ogre Psalm33

3
@Chris Zaten jenerik ilaç kullandığınız için onları da kullanabilirsiniz - yani ham türleri kullanmamak Comparable. <T extends Comparable<? super T>>Bunun yerine deneyin .
easoncxz

15

doIt()Yönteminizi bildirirken jenerik yöntemler için sözdizimini kullanarak istediğinizi yapmak mümkündür ( yöntem imzası <T>arasında staticve arasına eklenmesine dikkat edin ):voiddoIt()

class Clazz<T> {
  static <T> void doIt(T object) {
    // shake that booty
  }
}

Eclipse editörünü yukarıdaki kodu hatasız olarak kabul ettim Cannot make a static reference to the non-static type Tve daha sonra aşağıdaki çalışma programına (yaşa uygun kültürel referansla tamamlandı) genişlettim:

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }

  private static class KC {
  }

  private static class SunshineBand {
  }

  public static void main(String args[]) {
    KC kc = new KC();
    SunshineBand sunshineBand = new SunshineBand();
    Clazz.doIt(kc);
    Clazz.doIt(sunshineBand);
  }
}

Çalıştırdığımda bu satırları konsola yazdırır:

o ganimet 'sınıf com.eclipseoptions.datamanager.Clazz $ KC' sallamak !!!
o ganimet 'sınıf com.eclipseoptions.datamanager.Clazz $ SunshineBand' sallamak !!!


Bu durumda, ikinci <T> ilkini gizliyor mu?
André Chalella

1
@ AndréNeves, Evet. İkincisi <T>, ilkini class C { int x; C(int x) { ... } }parametrede xalanı maskelediği şekilde maskeler x.
Mike Samuel

Numunenin çıktısı nasıl açıklanır: bir tane parametre T değerine göre gerçekten iki tür söz konusudur? Eğer T gerçekten sınıf tipini saklıyor olsaydı "shay that booty 'sınıfı com.eclipseoptions.datamanager.Clazz" parametresiz iki kez çıktı çıktı.
Pragmateek

1
Maskeleme ile ilgisi yoktur. Statik bir yöntem , sınıfların genel türü tarafından bağlanamaz . Bununla birlikte, kendi genel türlerini ve sınırlarını tanımlayabilir.
mike

Statik türü bir kez tanımlamanın ve birkaç statik yöntem için yeniden kullanmanın bir yolu var mı? Ben static <E extends SomeClass> E foo(E input){return input;}gibi bir şey yapmak istediğinizde her yöntem için bir hayranı değilimstatic <E extends SomeClass>; //static generic type defined only once and reused static E foo(E input){return input;} static E bar(E input){return input;} //...etc...
HesNotTheStig

14

Bu sözdiziminin henüz belirtilmediğini düşünüyorum (argümanları olmayan bir yöntem istiyorsanız):

class Clazz {
  static <T> T doIt() {
    // shake that booty
  }
}

Ve çağrı:

String str = Clazz.<String>doIt();

Umarım bu birine yardım eder.


1
Aslında (Java sürümünü bilmiyorum), bu bile olmadan mümkündür <String>çünkü bir değişkene atadığınız için sadece tür argümanını ihlal eder. Eğer atamazsanız, sadece ihlal eder Object.
Adowrath

Bu mükemmel bir çözüm.
Prabhat Ranjan

6

Hatada doğru şekilde belirtilmiştir: statik olmayan T türü için statik başvuru yapamazsınız. Bunun nedeni, type parametresinin T, örneğin Clazz<String>veya türündeki herhangi bir tür bağımsız değişkeniyle değiştirilebilir Clazz<integer>. Ancak statik alanlar / yöntemler, -Sınıfın statik nesneleri.

Aşağıdaki alıntı dokümandan alınmıştır :

Bir sınıfın statik alanı, sınıfın statik olmayan tüm nesneleri tarafından paylaşılan sınıf düzeyinde bir değişkendir. Bu nedenle, tür parametrelerinin statik alanlarına izin verilmez. Aşağıdaki sınıfı düşünün:

public class MobileDevice<T> {
    private static T os;

    // ...
}

Tür parametrelerinin statik alanlarına izin verildiyse, aşağıdaki kod karıştırılır:

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

Statik alan işletim sistemi telefon, çağrı cihazı ve bilgisayar tarafından paylaşıldığından, işletim sisteminin gerçek türü nedir? Akıllı Telefon, Çağrı Cihazı ve TabletPC aynı anda olamaz. Bu nedenle, tür parametrelerinin statik alanlarını oluşturamazsınız.

Chris'in cevabında haklı olarak işaret ettiği gibi , bu durumda sınıfla değil, yöntemle type parametresini kullanmanız gerekir. Gibi yazabilirsiniz:

static <E> void doIt(E object) 

3

Aşağıdaki gibi bir şey sizi yakınlaştıracaktır

class Clazz
{
   public static <U extends Clazz> void doIt(U thing)
   {
   }
}

DÜZENLEME: Daha ayrıntılı olarak güncellenen örnek

public abstract class Thingo 
{

    public static <U extends Thingo> void doIt(U p_thingo)
    {
        p_thingo.thing();
    }

    protected abstract void thing();

}

class SubThingoOne extends Thingo
{
    @Override
    protected void thing() 
    {
        System.out.println("SubThingoOne");
    }
}

class SubThingoTwo extends Thingo
{

    @Override
    protected void thing() 
    {
        System.out.println("SuThingoTwo");
    }

}

public class ThingoTest 
{

    @Test
    public void test() 
    {
        Thingo t1 = new SubThingoOne();
        Thingo t2 = new SubThingoTwo();

        Thingo.doIt(t1);
        Thingo.doIt(t2);

        // compile error -->  Thingo.doIt(new Object());
    }
}

Tam Jason S'nin önerdiği gibi.
André Chalella

@Ve önceki öneri, U'nun Clazz'ı uzatması gerektiğine dair bir kısıtlama getirmedi
ekj

Anlıyorum, ama kısıtlama dışında faydalı bir şey eklemiyor. Orijinal yazımda, U parametresini geçmeden bunu yapabilmek istiyorum.
André Chalella

@Andre Yukarıdaki güncellememe bakın. Genel tür yalnızca yöntem tanımında tanımlanır ve yöntem
çağrılırken

@ekj Teşekkürler, şimdi anlıyorum. Üzgünüm, bu soruyu üç yıl önce günlük Java yaparken sormuştum. Fikrinizi anladım, ancak aslında bunun tam olarak istediğim şey olmadığını anladığınızı düşünüyorum. Ancak cevabınızı beğendim.
André Chalella

2

Sınıfınız için genel bir tür belirttiğinizde, JVM bu sınıfın tanımının değil, yalnızca sınıfınızın bir örneğine sahip olduğunu bilir. Her tanım sadece parametreli tiptedir.

Jenerikler C ++ 'da şablonlar gibi çalışır, bu yüzden önce sınıfınızı başlatmalı, sonra işlevi belirtilen türle kullanmalısınız.


2
Java jenerikleri C ++ şablonlarından oldukça farklıdır. Genel sınıf kendi başına derlenir. Aslında C ++ 'daki eşdeğer kod işe Clazz<int>::doIt( 5 )
yarar

Ben bu cevap kabul edilen bir daha iyi gibi ... C ++ şablon referans dışında, genel tür hakkında yorum tüm sınıf yerine sadece sınıfın bir örneği için olmak spot on. Kabul edilen cevap bunu açıklamaz ve sadece sınıfın genel türünü statik bir yöntemde neden kullanamayacağınızla ilgili olmayan bir geçici çözüm sağlar.
Jorn

1

Ayrıca çünkü tanımlamak rağmen o generics.Which araçlarının "Silme" özelliğinin olur, basit terimlerle ifade etmek ArrayList<Integer>ve ArrayList<String>derleme zamanında, iki farklı beton türleri olarak kalır ama zamanında JVM jenerik türleri ve siler iki sınıf yerine yalnızca bir ArrayList sınıfı oluşturur. Dolayısıyla, bir statik tür yöntemi veya bir jenerik için herhangi bir şey tanımladığımızda, bu jenerikin tüm örnekleri tarafından paylaşılır, benim örneğimde her ikisi tarafından da paylaşılır ArrayList<Integer>ve ArrayList<String>bu nedenle hatayı alırsınız. Statik Bağlamda İzin Verildi!


1

Rivenhill'de @BD: Bu eski soru geçen yıl yeniden ilgi gördüğü için, sadece tartışma uğruna biraz ilerleyelim. Metodunuzun gövdesi doIthiçbir şey yapmaz T-özellikle. İşte burada:

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

Böylece tüm tip değişkenlerini tamamen bırakabilir ve sadece kod yazabilirsiniz.

public class Clazz {
  static void doIt(Object object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

Tamam. Ama asıl soruna geri dönelim. Sınıf bildirimindeki ilk tür değişkeni gereksizdir. Sadece yöntemde ikincisine ihtiyaç vardır. İşte yine başlıyoruz, ama son cevap henüz değil:

public class Clazz  {
  static <T extends Saying> void doIt(T object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}
// Output:
// KC
// Sunshine

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

Ancak, aşağıdaki sürüm aynı şekilde çalıştığından, hiçbir şey hakkında çok fazla karışıklık var. Tek ihtiyacı olan method parametresindeki arabirim türüdür. Görünürde hiçbir yerde değişken yok. Bu gerçekten asıl sorun muydu?

public class Clazz  {
  static void doIt(Saying object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

0

Statik değişkenler sınıfın tüm örnekleri tarafından paylaşıldığından. Örneğin, aşağıdaki kodunuz varsa

class Class<T> {
  static void doIt(T object) {
    // using T here 
  }
}

T yalnızca bir örnek oluşturulduktan sonra kullanılabilir. Ancak, örnekler kullanılmadan önce bile statik yöntemler kullanılabilir. Bu nedenle, Genel tür parametrelerine statik yöntemler ve değişkenler içinde başvurulamaz

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.