Java Adı Gizleme: Zor Yol


96

Ad gizlemeyle ilgili çözmesi son derece zor bir sorunum var. Sorunu açıklayan basitleştirilmiş bir versiyon:

Bir sınıf var: org.A

package org;
public class A{
     public class X{...}
     ...
     protected int net;
}

Sonra bir sınıf var net.foo.X

package net.foo;
public class X{
     public static void doSomething();
}

Ve şimdi, buradan miras alan Ave aramak isteyen sorunlu sınıf.net.foo.X.doSomething()

package com.bar;
class B extends A {

    public void doSomething(){
        net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
        X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
    }
}

Gördüğünüz gibi bu mümkün değil. XKalıtsal bir tür tarafından gizlendiği için basit adı kullanamıyorum . Tam adı kullanamıyorum net.foo.Xçünkü netdevralınan bir alan tarafından gizlenmiş durumda.

BKod tabanımda yalnızca sınıf var; sınıflar net.foo.Xve org.Akütüphane sınıfları, bu yüzden onları değiştiremem!

Tek çözümüm şuna benziyor: Sırayla arayan başka bir sınıfı arayabilirim X.doSomething(); ancak bu sınıf, çok dağınık görünen isim çatışması nedeniyle var olabilirdi! Ben doğrudan çağrı hangi çözüm var mı X.doSomething()gelen B.doSomething()?

Global isim alanının belirlenmesine izin veren bir dilde, örneğin global::C # veya ::C ++ 'da, basitçe netbu genel önekle önek alabilirim, ancak Java buna izin vermiyor.


Bazı hızlı düzeltmeler şu olabilir: public void help(net.foo.X x) { x.doSomething(); }ve ile arayınhelp(null);
Absurd-Mind

@ Absurd-Mind: Var olmayan bir nesne üzerinden statik bir yöntemi çağırmak benim çözümümden bile daha karmaşık görünüyor :). Hatta bir derleyici uyarısı alacağım. Ama doğru, bu benimkine ek olarak başka bir hacky çözüm olacaktır.
gexicide

@JamesB: Bu yanlış X'i gösterir! net.foo.Xyöntemi var, değil org.A.X!
gexicide

3
Eğer Do sahip devralır ila A? Kalıtım çok kötü olabilir, bulduğunuz gibi…
Donal Fellows

2
I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messyTemiz kod tutumu için +1. Ama benim için bu, değiş tokuş yapmanız gereken bir durum gibi görünüyor. Basitçe bunu yapın ve neden bunu yapmak zorunda olduğunuz hakkında uzun güzel bir yorum yapın (muhtemelen bu soruya bir bağlantıyla).
sampathsris

Yanıtlar:


84

Türe bir nullatayabilir ve ardından yöntemi çağırabilirsiniz (hedef nesne statik yöntemlerin çağrılmasına dahil olmadığından işe yarar).

((net.foo.X) null).doSomething();

Bunun faydaları var

  • yan etkisiz olma (örneklemeyle ilgili bir sorun net.foo.X),
  • herhangi bir şeyin yeniden adlandırılmasını gerektirmez (böylece yöntemi, Bsahip olmasını istediğiniz adda verebilirsiniz ; bu nedenle import static, sizin durumunuzda a çalışmaz),
  • delege sınıfının tanıtılmasını gerektirmeyen (ancak bu iyi bir fikir olabilir…) ve
  • yansıma API'si ile çalışmanın ek yükünü veya karmaşıklığını gerektirmez.

Olumsuz yanı, bu kodun gerçekten korkunç olmasıdır! Benim için bir uyarı yaratıyor ve bu genel olarak iyi bir şey. Ancak, aksi takdirde tamamen pratik olmayan bir sorunun etrafında çalıştığı için,

@SuppressWarnings("static-access")

uygun (minimum!) bir kapalı noktada derleyiciyi kapatır.


1
Neden olmasın? Sadece doğru olanı yaparsınız ve kodu yeniden düzenlersiniz.
Gimby

1
Statik ithalatı çok bu çözümün daha net olduğunu değil ve her durumda değil işi (bir var mı, mahvolduk doSomething, hiyerarşinizdeki yöntem her yerde) bu yüzden evet iyi çözüm.
Voo

@Voo Aslında ilk sorunun tam olarak ne olduğunu yeniden üreten diğer insanların cevaplar olarak listeledikleri tüm seçenekleri gerçekten gittiğimi ve denediğimi özgürce itiraf ediyorum ve etrafta çalışmanın ne kadar zor olduğuna şaşırdım. Orijinal soru, diğer tüm daha hoş seçenekleri düzgün bir şekilde kapatır.
Donal Fellows

1
Yaratıcılık için oy verin, yine de bunu üretim kodunda asla yapmam. Doğruluğun bu sorunun cevabı olduğuna inanıyorum (tüm problemler için değil mi?).
ethanfar

Bu çözüm için teşekkürler Donal. Aslında bu çözüm, bir "Tip" in kullanılabileceği ve bir değişkenin kullanılamayacağı yerleri vurgular. Bu nedenle, bir "döküm" de, aynı ada sahip bir değişken olsa bile derleyici "Tipi" seçecektir. Bu tür daha ilginç durumlar için, 2 "Java Tuzakları" kitabını tavsiye ederim: books.google.co.in/books/about/… books.google.co.in/books/about/…
RRM

38

Muhtemelen bunu yönetmenin en basit (en kolay yolu değil) yolu bir temsilci sınıfıyla olacaktır:

import net.foo.X;
class C {
    static void doSomething() {
         X.doSomething();
    }
}

ve sonra ...

class B extends A {
    void doX(){
        C.doSomething();
    }
}

Bu biraz ayrıntılı ama çok esnektir - istediğiniz gibi davranmasını sağlayabilirsiniz; artı hem staticyöntemlerle hem de örneklenmiş nesnelerle aynı şekilde çalışır

Temsilci nesneler hakkında daha fazla bilgiyi burada bulabilirsiniz: http://en.wikipedia.org/wiki/Delegation_pattern


Muhtemelen sağlanan en temiz çözüm. Bu problemim olsaydı muhtemelen onu kullanırdım.
LordOfThePigs

37

Statik bir içe aktarma kullanabilirsiniz:

import static net.foo.X.doSomething;

class B extends A {
    void doX(){
        doSomething();
    }
}

Buna dikkat edin Bve Aadlandırılmış yöntemler içermeyindoSomething


Net.foo.X.doSomething'in yalnızca paket erişimi yok mu? Bu, com.bar paketinden erişemeyeceğiniz anlamına gelir
JamesB

2
@JamesB Evet, ancak o zaman tüm soru bir anlam ifade etmeyecektir, çünkü yöntem hiçbir şekilde erişilebilir olmayacaktır. Örneği sadeleştirirken bunun bir hata olduğunu düşünüyorum
Absurd-Mind

1
Fakat asıl soru aktif kullanmak istediğiniz gibi görünüyor doSomethingiçinde yöntemin adı olarak B...
Donal Fellows

3
@DonalFellows: Haklısınız; kodumun gönderdiğim gibi kalması gerekiyorsa bu çözüm işe yaramaz. Neyse ki, yöntemi yeniden adlandırabilirim. Ancak, yöntemimin başka bir yöntemi geçersiz kıldığı bir durumu düşünün, o zaman onu yeniden adlandıramıyorum. Bu durumda, bu cevap aslında sorunu çözmeyecektir. Ama bu zaten benim sorunumdan daha da tesadüfi bir şekilde olurdu, bu yüzden belki de üçlü isim çatışması ile böyle bir problemle karşılaşacak bir insan asla olmayacak :).
gexicide

4
Herkes için açıklığa kavuşturmak için: Bu çözüm doSomething, miras hiyerarşisinde herhangi bir yere sahip olduğunuz anda çalışmaz (yöntem çağrısı hedeflerinin nasıl çözüldüğüne bağlı olarak). Bir toStringyöntem çağırmak istiyorsanız Knuth size yardımcı olur . Donal tarafından önerilen çözüm, Bloch'un Java Puzzlers kitabında bulunan çözümdür (sanırım, bakmadım), bu yüzden bunu gerçekten güvenilir bir cevap olarak kabul edebiliriz :-)
Voo

16

İşleri yapmanın doğru yolu statik içe aktarma olacaktır, ancak mutlak en kötü senaryoda, tam olarak nitelenmiş adını biliyorsanız, yansıma kullanarak sınıfın bir örneğini oluşturabilirdiniz.

Java: varsayılan kurucusu olmayan sınıfın newInstance'ı

Ve sonra örnekteki yöntemi çağırın.

Ya da sadece yansıma ile yöntemin kendisini çağırın: Yansıma kullanarak statik bir yöntemi çağırmak

Class<?> clazz = Class.forName("net.foo.X");
Method method = clazz.getMethod("doSomething");
Object o = method.invoke(null);

Tabii ki bunlar son çareler.


2
Bu cevap şu ana kadar çok büyük overkill gereğidir - başparmak yukarıya :)
gexicide

3
Bu cevap için bir tamamlayıcı rozeti almalısınız; Java'nın eski bir sürümünü kullanmak ve tam da bu sorunu çözmek zorunda olan o tekil fakir kişiye yanıt verdiniz.
Gimby

Aslında bu static importözelliğin sadece içinde eklendiğini düşünmedim Java 1.5. 1.4 veya daha düşük seviyelerde gelişmesi gereken insanları kıskanmıyorum, bir kez yapmak zorunda kaldım ve korkunçtu!
EpicPandaForce

1
@Gimby: Eski java'da hala çalışan bir ((net.foo.X)null).doSomething()çözüm var. Ancak tip Abile bir iç tip içeriyorsa net, SONRA bu geçerli kalan tek cevaptır :).
gexicide

6

Gerçekten cevap değil, ancak bir X örneği oluşturabilir ve üzerinde statik yöntemi çağırabilirsiniz. Bu, yönteminizi çağırmanın bir yolu olabilir (itiraf ediyorum kirli).

(new net.foo.X()).doSomething();

3
Türe çevirmek nullve ardından yöntemi çağırmak daha iyi olurdu, çünkü bir nesne oluşturmanın istemediğim yan etkileri olabilir.
gexicide

@gexicide Bu döküm fikri, nullbunu yapmanın en basit yollarından biri gibi görünüyor. Uyarısız @SuppressWarnings("static-access")olması gerekiyor…
Donal Fellows

Hassasiyet için teşekkürler null, ancak döküm benim nulliçin her zaman bir kalp kırıcı olmuştur.
Michael Laffargue

4

Herhangi bir değişiklik yapmanıza veya garip uyarıları bastırmanıza veya gereksiz bir örnek oluşturmanıza gerek yoktur. Alt sınıf aracılığıyla üst sınıf statik yöntemlerini çağırabileceğiniz gerçeğini kullanan bir numara. ( Buradaki hile çözümüme benzer .)

Sadece bunun gibi bir sınıf oluşturun

public final class XX extends X {
    private XX(){
    }
}

(Bu son sınıftaki özel kurucu, hiç kimsenin yanlışlıkla bu sınıfın bir örneğini oluşturamayacağından emin olur.)

O zaman onun X.doSomething()üzerinden aramakta özgürsünüz :

    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }

İlginç, yine başka bir yaklaşım. Bununla birlikte, statik yönteme sahip sınıf, son bir yardımcı sınıftır.
gexicide

Evet, bu durumda bu numarayı kullanamazsın. Statik yöntemin bir dilin başarısız olmasının bir başka nedeni de Scala'nın nesne yaklaşımı benimsenmelidir.
billc.cn

Yine de başka bir sınıf oluşturacaksanız, blgt yanıtında açıklandığı gibi bir temsilci sınıfı kullanmak muhtemelen en iyisidir.
LordOfThePigs

@LordOfThePigs Bu, fazladan yöntem çağrısını ve gelecekteki yeniden düzenleme yükünü ortadan kaldırır. Yeni sınıf temelde bir takma ad görevi görür.
billc.cn

2

Ya tüm dosyalar aynı klasörde olduğu için gobalnamespace'i almaya çalışırsanız ne olur? ( http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html )

    package com.bar;
      class B extends A {

       public void doSomething(){
         com.bar.getGlobal().net.foo.X.doSomething(); // drill down from the top...

         }
     }

Bu nasıl çalışıyor? AFAIK getGlobal()(ı ... paketler Java herhangi yöntemleri olamaz düşünüyorum) ... paketleri standart bir Java yöntemi değildir
Siegi

1

Bu, kompozisyonun kalıtıma tercih edilmesinin nedenlerinden biridir.

package com.bar;
import java.util.concurrent.Callable;
public class C implements Callable<org.A>
{
    private class B extends org.A{
    public void doSomething(){
        C.this.doSomething();
    }
    }

    private void doSomething(){
    net.foo.X.doSomething();
    }

    public org.A call(){
    return new B();
    }
}

0

Strateji modelini kullanırdım.

public interface SomethingStrategy {

   void doSomething();
}

public class XSomethingStrategy implements SomethingStrategy {

    import net.foo.X;

    @Override
    void doSomething(){
        X.doSomething();
    }
}

class B extends A {

    private final SomethingStrategy strategy;

    public B(final SomethingStrategy strategy){
       this.strategy = strategy;
    }

    public void doSomething(){

        strategy.doSomething();
    }
}

Artık bağımlılığınızı da ayırdınız, böylece birim testlerinizi yazmak daha kolay olacaktır.


Bir eklemek unuttuysanız implements SomethingStrategyüzerinde XSomethingStrategysınıf bildirimi.
Ricardo Souza

@rcdmk Teşekkürler. Şimdi düzeltilmelidir.
Erik Madsen
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.