İnstanceof operatörünü bir switch deyiminde kullanmak mümkün müdür?


267

instanceofNesne için anahtar durumu kullanarak bir sorum var :

Örneğin: sorunum Java ile çoğaltılabilir:

if(this instanceof A)
    doA();
else if(this instanceof B)
    doB();
else if(this instanceof C)
    doC():

Nasıl kullanılır switch...case?


6
Eğer gerçekten bir anahtara ihtiyacınız olduğunu düşünüyorsanız, sınıf adını bir int'e ekleyebilir ve kullanabilirsiniz, ancak olası çatışmalara dikkat edin. Aslında bu fikrin kullanılmasını sevmediğim için bir cevap yerine yorum olarak eklemek. Belki de gerçekten ihtiyacınız olan ziyaretçi kalıbıdır.
vickirk

1
Java 7'den itibaren @vickirk'in işaret ettiği gibi karma çatışmalardan kaçınmak için tam nitelikli sınıf adını bile açabilirsiniz, ancak yine de çirkin.
Mitja

Yanıtlar:


225

Bu, alt tip polimorfizmin yardımcı olduğu tipik bir senaryodur. Aşağıdakileri yapın

interface I {
  void do();
}

class A implements I { void do() { doA() } ... }
class B implements I { void do() { doB() } ... }
class C implements I { void do() { doC() } ... }

Sonra sadece çağırabilir do()üzerinde this.

Değiştirmek ücretsiz değilseniz A, Bve C, aynı ulaşmak için ziyaretçi desen uygulanabilir.


33
Ziyaretçi kalıbı, A, B ve C'nin Ziyaretçiyi giriş parametresi olarak alan soyut bir yöntemle bir arayüz uygulamak zorunda olduğu anlamına gelir; A, B, C'yi değiştiremezseniz ve hiçbiri bu arayüzü uygulamazsa ne olur?
thermz

21
Ziyaretçi kalıbı ile ilgili son yorum yanlış. Yine de A, B ve C'nin bir arabirim uygulamasını sağlamanız gerekir.
Ben Thurley

10
Ne yazık ki, do () - Kodu ana bilgisayarın ortamını gerektiriyorsa çalışmaz (yani do () öğesinde bulunmayan değişkenlere erişim).
mafu

2
@mafu OP'nin sorusu tür tabanlı gönderim hakkındaydı. Do () yönteminizin gönderilmesi için sorununuzdan daha fazla girdiye ihtiyacı varsa IMHO burada tartışılan sorunun kapsamı dışındadır.
jmg

3
Bu cevap, A, B, C sınıflarını değiştirebileceğinizi varsayarken, nokta, A, B, C'yi değiştirmeden nasıl yapılacağını düşünüyorum, çünkü üçüncü bir parça kütüphanesinde olabilirler
cloudy_weather

96

bir arabirime kesinlikle kod yazamıyorsanız, aracı olarak bir enum kullanabilirsiniz:

public A() {

    CLAZZ z = CLAZZ.valueOf(this.getClass().getSimpleName());
    switch (z) {
    case A:
        doA();
        break;
    case B:
        doB();
        break;
    case C:
        doC();
        break;
    }
}


enum CLAZZ {
    A,B,C;

}

thx, ben de bazı değişiklikler yapmak zorunda kaldı: 1) Sınıf numaralandırma ile her numaralandırma id başlatmak; 2) sınıf basit adını enum id .toString () ile iddia edin; 3) numaralandırmayı, numaralandırma kimliği başına depolanan Sınıf referansı aracılığıyla bulun. Bence bu da o zaman gizliliğe açık.
Kova Gücü

this.getClass (). getSimpleName () bir CLAZZ değeriyle eşleşmezse bir İstisna atar ... bir try catch bloğu ile çevrelemek daha iyidir ve İstisna "varsayılan" veya "başka" seçeneği olarak kabul edilir anahtarı
tetri

40

Sadece sınıfın anahtar olduğu ve lambda ya da benzeri işlevin değer olduğu bir Harita oluşturun.

Map<Class,Runnable> doByClass = new HashMap<>();
doByClass.put(Foo.class, () -> doAClosure(this));
doByClass.put(Bar.class, this::doBMethod);
doByClass.put(Baz.class, new MyCRunnable());

// elbette, bunu yalnızca bir kez başlatmak için yeniden düzenleyin

doByClass.get(getClass()).run();

İstisnaları atan ve bir Runnable yerine bunu kullanan bir FunctionalInterface uygulamak yerine İstisnalar işaretli gerekiyorsa.


3
En iyi çözüm imho, özellikle kolay yeniden düzenleme sağlar.
Feiteira

2
Bu çözümün tek dezavantajı, lambdalarda (elbette gerekli olabileceği varsayılarak) yerel (yöntem) değişkenleri kullanamamasıdır.
zapatero

1
@zapatero Runnable yerine Function işlevini değiştirebilir, örneği parametre olarak iletebilir, sonra gerektiğinde
yayınlayabilirsiniz

Upvoted; bu aslında OP'nin istediği şeyi yapmasına yardımcı olan birkaç cevaptan biridir (ve evet, kodu yapmak zorunda kalmamak için yeniden düzenlemek genellikle mümkündür instanceofve hayır, benim senaryom maalesef kolayca uyanlardan biri değil bu kutu ...)
Per Lundberg

@ SergioGutiérrez Teşekkürler. Bu desene sadece harici bir kütüphane ile ihtiyaç duyulmalıdır. Ve o zaman bile, bunun yerine adaptör uygulamasıyla bir arayüz oluşturabilirsiniz, ancak davranışsal DIFF'ın, daha açık bir şekilde ifade etmek istediğinizde yararlıdır. Akıcı vs ek açıklama API yönlendirmesine benzer sanırım.
Novaterata

36

Birisi okuyacaksa:

Java'daki EN İYİ çözüm:

public enum Action { 
    a{
        void doAction(...){
            // some code
        }

    }, 
    b{
        void doAction(...){
            // some code
        }

    }, 
    c{
        void doAction(...){
            // some code
        }

    };

    abstract void doAction (...);
}

Bu modelin BÜYÜK faydaları:

  1. Sadece şöyle yapın (HİÇBİR anahtar yok):

    void someFunction ( Action action ) {
        action.doAction(...);   
    }
  2. "D" adlı yeni bir Eylem eklerseniz, doAction (...) yöntemini kaldırmanız GEREKİR

NOT: Bu desen Joshua'nın Bloch "Etkili Java (2. Baskı)" bölümünde açıklanmıştır


1
Güzel! Is @Overrideher uygulanmasına yukarıda gerekli doAction()?
mateuscb

9
Bu nasıl "EN İYİ" çözüm? Hangisini actionkullanacağınıza nasıl karar verirsiniz ? someFunction()Doğru ile çağıran dış bir instanceof-cascade tarafından actionmı? Bu sadece başka bir dolaylılık seviyesi ekler.
PureSpider

1
Hayır, çalışma zamanında otomatik olarak yapılacaktır. SomeFunction (Action.a) öğesini çağırırsanız a.doAction çağrılacaktır.
se.solovyev

11
Bunu anlamıyorum. Hangi numaralandırmayı kullanacağınızı nasıl bilebilirsiniz? @PureSpider'ın dediği gibi, bu sadece yapılacak başka bir iş seviyesi gibi görünüyor.
James Manes

2
Eksiksiz bir örnek sunmamanız çok üzücü , örn. A, b veya C Sınıf Örneklerinin bu numaralandırma ile nasıl eşleştirildiği. Örneği bu Numaralandırmaya aktarmaya çalışacağım.
Tom

21

Yapamazsın. switchİfadesi yalnızca içerebilir casederleme zamanı sabitleri ve bir tamsayı (Java 6 adet olmak üzere ve Java 7 bir dize) için değerlendirmek ifadeleri.

Aradığınız programlamaya fonksiyonel olarak "desen eşleştirme" denir.

Ayrıca bkz.Java'da örneklemekten kaçınma


1
Hayır, çoğu işlevsel dilde türler üzerinde eşleştirme yapamazsınız, yalnızca yapıcılarda. Bu en azından ML ve Haskell için geçerlidir. Scala ve OCaml'de desen eşleşmesinin tipik uygulaması mümkün değildir.
jmg

Elbette, ancak yapıcılara karşı denetim, yukarıda açıklanan senaryoya "eşdeğer" olacaktır.
Carlo V.Dango

1
Bazı durumlarda, ancak genel olarak değil.
jmg

Anahtarlar numaralandırmaları da destekleyebilir.
Solomon Ucko

Farklı bir dile bakmak "yapamazsınız" nadiren yardımcı bir cevaptır.
L. Blanc

17

Üst yanıtlarda tartışıldığı gibi, geleneksel OOP yaklaşımı anahtar yerine polimorfizm kullanmaktır. Bu hile için iyi belgelenmiş bir yeniden düzenleme modeli bile vardır: Koşulluları Polimorfizm ile Değiştir . Ne zaman bu yaklaşım için ulaşmak, ben de varsayılan davranış sağlamak için bir boş nesne uygulamak istiyorum .

Java 8'den başlayarak, fonksiyonel programcıların tanıdık oldukları bir şey vermek için lambdas ve jenerikleri kullanabiliriz: desen eşleştirme. Temel dil özelliği değildir, ancak Javaslang kütüphanesi bir uygulama sağlar. Javadoc'tan bir örnek :

Match.ofType(Number.class)
    .caze((Integer i) -> i)
    .caze((String s) -> new BigDecimal(s))
    .orElse(() -> -1)
    .apply(1.0d); // result: -1

Java dünyasındaki en doğal paradigma değil, bu yüzden dikkatli kullanın. Genel yöntemler sizi eşleşen değeri tahmin etmek zorunda kalmayacak olsa da, örneğin eşleşen nesneyi Scala'nın vaka sınıflarında olduğu gibi ayrıştırmanın standart bir yolunu kaçırıyoruz .


9

Bunun çok geç olduğunu biliyorum ama gelecekteki okuyucular için ...

Sadece A , B , C sınıfının adına dayanan yukarıdaki yaklaşımlara dikkat edin :

Bunu garanti sürece A , B , C ... (bütün alt sınıflar veya uygulayıcıları Base ) olan nihai sonra alt sınıfları A , B , C ... ele alınmayacaktır.

Olsa bile, elseif, elseif .. yaklaşım daha doğrudur, alt sınıflar / Uygulayıcıların sayıda yavaştır.


Gerçekten de, polimorfizm (aka OOP) kullanmamalısınız
Val

8

Maalesef, switch-case deyimi sabit bir ifade beklediğinden kutunun dışında mümkün değildir. Bunun üstesinden gelmek için bir yol sınıf numaralarıyla enum değerlerini kullanmak olacaktır.

public enum MyEnum {
   A(A.class.getName()), 
   B(B.class.getName()),
   C(C.class.getName());

private String refClassname;
private static final Map<String, MyEnum> ENUM_MAP;

MyEnum (String refClassname) {
    this.refClassname = refClassname;
}

static {
    Map<String, MyEnum> map = new ConcurrentHashMap<String, MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
        map.put(instance.refClassname, instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
}

public static MyEnum get(String name) {
    return ENUM_MAP.get(name);
 }
}

Bununla bu şekilde switch deyimini kullanmak mümkündür

MyEnum type = MyEnum.get(clazz.getName());
switch (type) {
case A:
    ... // it's A class
case B:
    ... // it's B class
case C:
    ... // it's C class
}

JEP sorunu 8213076 tamamen uygulanana kadar, bu hedef sınıfı değiştirmeden, türe dayalı bir anahtar deyimi almak için en temiz yolu olduğuna inanıyorum.
Rik Schaaf


5

Bunun gibi switch deyimlerini kullanmak nesne yönelimli bir yol değildir. Bunun yerine polimorfizmin gücünü kullanmalısınız . Basitçe yazın

this.do()

Daha önce bir temel sınıf oluşturmuş olmanız durumunda:

abstract class Base {
   abstract void do();
   ...
}

bunun için temel sınıftır A, Bve C:

class A extends Base {
    void do() { this.doA() }
}

class B extends Base {
    void do() { this.doB() }
}

class C extends Base {
    void do() { this.doC() }
}

@jmg suggeests ( stackoverflow.com/questions/5579309/switch-instanceof/… ) soyut bir temel sınıf yerine bir arabirim kullanarak. Bazı sirklerde bu daha üstün olabilir.
Raedwald

5

Bu, daha hızlı çalışacak ve
göreceli olarak çok sayıda ' vakanız' olması durumunda
performansa duyarlı bağlamda süreç yürütülecektir

public <T> T process(Object model) {
    switch (model.getClass().getSimpleName()) {
        case "Trade":
            return processTrade();
        case "InsuranceTransaction":
            return processInsuranceTransaction();
        case "CashTransaction":
            return processCashTransaction();
        case "CardTransaction":
            return processCardTransaction();
        case "TransferTransaction":
            return processTransferTransaction();
        case "ClientAccount":
            return processAccount();
        ...
        default:
            throw new IllegalArgumentException(model.getClass().getSimpleName());
    }
}

1
Bu bir örnekleme yapmakla aynı şey değildir, çünkü bu yalnızca uygulama sınıfı geçiş yapmak için kullanılırsa çalışır, ancak arabirimler / soyut sınıf / üst sınıflar için çalışmaz
lifesoordinary

evet, bu değil
Mike

A çaba için, ancak @ lifesoordinary'nin yorumunun yanı sıra, normalde sahip olduğunuz güvenlik kurallarını da kaçırırsınız, çünkü bu cevap sınıf referansları yerine sabit kodlu dizeler kullanır. Bu Burada farklı paket names.Edit ile sınıf isimlerinde herhangi bir örtüşme olmadığını tam kurallı adları ile bu işlevselliği genişletmek gerekiyordu, özellikle bir yazım hatası yapmak oldukça kolaydır: (tür beni doğruluyor) sabit yazım hatası
Rik Schaaf

4

Bir anahtar yalnızca bayt, kısa, char, int, String ve numaralandırılmış türlerle (ve ilkellerin nesne sürümleri ile çalışır, ayrıca java sürümünüze bağlıdır, Dizeler switchjava 7'de düzenlenebilir)


Java 6'da Dizeleri açamazsınız ve "temel öğelerin nesne sürümlerini" açamazsınız.
Lukas Eder

@Bozho Java sürümünüze bağlı olduğunu söyledim, Java 7'de Dizeleri açabilirsiniz.
Tnem

@Lukas Eder Java spec kontrol edebilirsiniz
Tnem

4

Şahsen aşağıdaki Java 1.8 kodunu beğendim:

    mySwitch("YY")
            .myCase("AA", (o) -> {
                System.out.println(o+"aa");
            })
            .myCase("BB", (o) -> {
                System.out.println(o+"bb");
            })
            .myCase("YY", (o) -> {
                System.out.println(o+"yy");
            })
            .myCase("ZZ", (o) -> {
                System.out.println(o+"zz");
            });

Çıktı olacak:

YYyy

Örnek kod Dizeler kullanır, ancak Sınıf dahil herhangi bir nesne türünü kullanabilirsiniz. Örneğin.myCase(this.getClass(), (o) -> ...

Aşağıdaki snippet'e ihtiyaç var:

public Case mySwitch(Object reference) {
    return new Case(reference);
}

public class Case {

    private Object reference;

    public Case(Object reference) {
        this.reference = reference;
    }

    public Case myCase(Object b, OnMatchDo task) {
        if (reference.equals(b)) {
            task.task(reference);
        }
        return this;
    }
}

public interface OnMatchDo {

    public void task(Object o);
}

4

Java artık OP biçiminde geçiş yapmanıza izin veriyor. Buna anahtar için Desen Eşleme diyorlar . Şu anda Taslak altında ama son zamanlarda anahtarlara ne kadar iş koyduklarını görünce bence geçecek. JEP'te verilen örnek

String formatted;
switch (obj) {
    case Integer i: formatted = String.format("int %d", i); break;
    case Byte b:    formatted = String.format("byte %d", b); break;
    case Long l:    formatted = String.format("long %d", l); break;
    case Double d:  formatted = String.format("double %f", d); break;
    case String s:  formatted = String.format("String %s", s); break
    default:        formatted = obj.toString();
}  

veya lambda sözdizimlerini kullanarak ve bir değer döndürerek

String formatted = 
    switch (obj) {
        case Integer i -> String.format("int %d", i)
        case Byte b    -> String.format("byte %d", b);
        case Long l    -> String.format("long %d", l); 
        case Double d  -> String.format("double %f", d); 
        case String s  -> String.format("String %s", s); 
        default        -> obj.toString();
    };

her iki durumda da anahtarlarla harika şeyler yapıyorlar.


3

Ortak arabirimi değiştirebiliyorsanız, bir numaralandırma ekleyebilir ve her sınıfın benzersiz bir değer döndürmesini sağlayabilirsiniz. Anlık veya ziyaretçi modeline ihtiyacınız olmayacak.

Benim için, mantığın nesnenin kendisi değil, switch deyiminde yazılı olması gerekiyordu. Bu benim çözümümdü:

ClassA, ClassB, and ClassC implement CommonClass

Arayüz:

public interface CommonClass {
   MyEnum getEnumType();
}

Sıralama:

public enum MyEnum {
  ClassA(0), ClassB(1), ClassC(2);

  private int value;

  private MyEnum(final int value) {
    this.value = value;
  }

  public int getValue() {
    return value;
  }

impl:

...
  switch(obj.getEnumType())
  {
    case MyEnum.ClassA:
      ClassA classA = (ClassA) obj;
    break;

    case MyEnum.ClassB:
      ClassB classB = (ClassB) obj;
    break;

    case MyEnum.ClassC:
      ClassC classC = (ClassC) obj;
    break;
  }
...

Java 7 kullanıyorsanız, numaralandırma için dize değerleri koyabilirsiniz ve anahtar kutusu bloğu yine de çalışır.


valueYalnızca enum sabitlerini ayırt etmek istiyorsanız alan gereksiz - doğrudan sabitlerini kullanabilirsiniz (gibi).
user905686

2

Buna ne dersin ?

switch (this.name) 
{
  case "A":
    doA();
    break;
  case "B":
    doB();
    break;
  case "C":
    doC();
    break;
  default:
    console.log('Undefined instance');
}

3
Bunun sadece Java 7'de çalıştığına dikkat etmelisiniz Ve aramak zorundasınız.Personel this.getSimpleName()JS ile karıştırılıp karıştırılmadığından emin değilsiniz (evet, konsol kullanıyor, hehe).
pablisco

5
Bunun kaynak kodu referans şeffaflığından düşme sorunu vardır. Yani, IDE'niz referans bütünlüğünü koruyamayacaktır. Adınızı yeniden adlandırmak istediğinizi varsayalım. Yansıma kötülüktür.
Val

1
Harika bir fikir değil. Birden çok sınıf yükleyiciniz varsa sınıf adları benzersiz değildir.
Doradus

Kod sıkıştırma söz konusu olduğunda kesiliyor (→ ProGuard)
Matthias Ronge

1

Bir anahtar deyimi kullanmanın nedenleri olduğunu düşünüyorum. Belki xText tarafından üretilen Kod kullanıyorsanız. Ya da başka bir tür EMF tarafından üretilen sınıflar.

instance.getClass().getName();

Sınıf Uygulama Adı Dizesini döndürür. ör .: org.eclipse.emf.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

basit temsili döndürür yani: EcoreUtil


Sabit değer olmadığı için koşul switcholarak kullanamazsınızcase
B-GangsteR

1

"Bu" nesnenin sınıf türünden "geçmeniz" gerekiyorsa, bu yanıt en iyisidir https://stackoverflow.com/a/5579385/2078368

Ancak başka bir değişkene "switch" uygulamanız gerekiyorsa. Başka bir çözüm öneririm. Aşağıdaki arayüzü tanımlayın:

public interface ClassTypeInterface {
    public String getType();
}

Bu arabirimi "değiştirmek" istediğiniz her sınıfa uygulayın. Misal:

public class A extends Something implements ClassTypeInterface {

    public final static String TYPE = "A";

    @Override
    public String getType() {
        return TYPE;
    }
}

Bundan sonra aşağıdaki şekilde kullanabilirsiniz:

switch (var.getType()) {
    case A.TYPE: {
        break;
    }
    case B.TYPE: {
        break;
    }
    ...
}

İlgilenmeniz gereken tek şey, "türleri" ClassTypeInterface uygulayan tüm sınıflar boyunca benzersiz tutun. Bu büyük bir sorun değildir, çünkü herhangi bir kavşak durumunda "switch-case" ifadesi için derleme zamanı hatası alırsınız.


Bunun için Dize kullanmak yerine, TYPEbir numaralandırma kullanabilirsiniz ve teklik garanti edilir ( bu cevapta olduğu gibi ). Bununla birlikte, herhangi bir yaklaşımla, bir yeniden adlandırma yaptığınızda iki yerde yeniden düzenleme yapmanız gerekecektir.
user905686

@ user905686 neyi yeniden adlandırıyor? Mevcut örnekte, kod miktarını en aza indirmek için Something sınıfında "A" türü tanımlanmıştır. Ancak gerçek hayatta bunu açıkça tanımlamanız gerekir (bazı ortak yerlerde) ve daha fazla yeniden düzenleme ile ilgili herhangi bir sorun yoktur.
Sergey Krivenkov

A sınıfı yeniden adlandırmak demek. Yeniden düzenleme sırasında otomatik yeniden düzenleme değişken içermeyebilir TYPE = "A". Özellikle karşılık gelen sınıfın dışındaysa, bunu manuel olarak yaparken de unutabilir. IntelliJ aslında sınıf adı dizelerinde veya yorumlarında da bulunur, ancak bu sadece bir metin aramasıdır (sözdizimi ağacına bakmak yerine) ve bu nedenle yanlış pozitifler içerir.
user905686

@ user905686 Bu fikri görselleştirmek için sadece bir örnek. String'i gerçek projede tür tanımları için kullanmayın, bazı MyTypes sınıf tutucularını tamsayı sabitleri (veya enum) ile bildirin ve bunları ClassTypeInterface uygulayan sınıflarda kullanın.
Sergey Krivenkov

1

İşte http://www.vavr.io/ kullanarak Java 8'de bunu gerçekleştirmenin işlevsel bir yolu

import static io.vavr.API.*;
import static io.vavr.Predicates.instanceOf;
public Throwable liftRootCause(final Throwable throwable) {
        return Match(throwable).of(
                Case($(instanceOf(CompletionException.class)), Throwable::getCause),
                Case($(instanceOf(ExecutionException.class)), Throwable::getCause),
                Case($(), th -> th)
        );
    }

1

Bir switch deyimi yazmak mümkün olmasa da, verilen her tip için belirli işlemlere dallanmak mümkündür. Bunu yapmanın bir yolu, standart çift sevk mekanizmasını kullanmaktır. Türe dayalı olarak "değiştirmek" istediğimiz bir örnek, çok sayıda istisnayı hata yanıtlarına eşlememiz gereken Jersey İstisna eşleştiricisidir. Bu özel durum için muhtemelen daha iyi bir yol olsa da (yani, her bir istisnayı bir hata yanıtına çeviren polimorfik bir yöntem kullanmak), çift gönderme mekanizması kullanmak hala yararlı ve pratiktir.

interface Processable {
    <R> R process(final Processor<R> processor);
}

interface Processor<R> {
    R process(final A a);
    R process(final B b);
    R process(final C c);
    // for each type of Processable
    ...
}

class A implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class B implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class C implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

Sonra "anahtar" gerekli olduğunda, bunu aşağıdaki gibi yapabilirsiniz:

public class LogProcessor implements Processor<String> {
    private static final Logger log = Logger.for(LogProcessor.class);

    public void logIt(final Processable base) {
        log.info("Logging for type {}", process(base));
    }

    // Processor methods, these are basically the effective "case" statements
    String process(final A a) {
        return "Stringifying A";
    }

    String process(final B b) {
        return "Stringifying B";
    }

    String process(final C c) {
        return "Stringifying C";
    }
}

Bu, zaten bu cevapta tartışılan Ziyaretçi desenine çok benziyor: stackoverflow.com/a/5579385
typeracer

0

Sınıf adlarıyla bir Numaralandırma oluşturun .

public enum ClassNameEnum {
    A, B, C
}

Nesnenin Sınıf adını bulun . Numaralandırma üzerine bir anahtar kutusu yazın .

private void switchByClassType(Object obj) {

        ClassNameEnum className = ClassNameEnum.valueOf(obj.getClass().getSimpleName());

        switch (className) {
            case A:
                doA();
                break;
            case B:
                doB();
                break;
            case C:
                doC();
                break;
        }
    }
}

Bu yardımcı olur umarım.


2
Enum sabitleri ve sınıflar arasındaki bağlantının açıkça yapıldığı bu yaklaşımın aksine , kaplini sınıf adına göre örtülü olarak yaparsınız. Bu, enum sabitinden veya sınıftan yalnızca birini yeniden adlandırdığınızda kodunuzu kıracak, oysa diğer yaklaşım yine de işe yarayacaktır.
user905686

0

Eclipse Modelleme Çerçevesi miras olarak da değerlendiren ilginç bir fikre sahiptir. Temel kavram Anahtar arayüzünde tanımlanır : Anahtarlama doSwitch yöntemi çağrılarak yapılır .

Gerçekten ilginç olan uygulama. Her bir ilgi türü için,

public T caseXXXX(XXXX object);

yönteminin uygulanması gerekir (varsayılan uygulama null döndürür). DoSwitch uygulaması arkadaşları aramayı dener caseXXX tüm tip hiyerarşisi için nesne üzerinde yöntemler. Çizgilerdeki bir şey:

BaseType baseType = (BaseType)object;
T result = caseBaseType(eAttribute);
if (result == null) result = caseSuperType1(baseType);
if (result == null) result = caseSuperType2(baseType);
if (result == null) result = caseSuperType3(baseType);
if (result == null) result = caseSuperType4(baseType);
if (result == null) result = defaultCase(object);
return result;

Gerçek çerçeve, her sınıf için bir tamsayı kimliği kullanır, bu nedenle mantık aslında saf bir anahtardır:

public T doSwitch(Object object) {
    return doSwitch(object.class(), eObject);
}

protected T doSwitch(Class clazz, Object object) {
    return doSwitch(getClassifierID(clazz), object);
}

protected T doSwitch(int classifierID, Object theObject) {
    switch (classifierID) {
    case MyClasses.BASETYPE:
    {
      BaseType baseType = (BaseType)object;
      ...
      return result;
    }
    case MyClasses.TYPE1:
    {
      ...
    }
  ...

Daha iyi bir fikir edinmek için ECoreSwitch'in eksiksiz bir uygulamasına bakabilirsiniz .


-1

instanceof kullanan bir anahtar yapısını taklit etmenin daha basit bir yolu var, bunu yönteminizde bir kod bloğu oluşturarak ve bir etiketle adlandırarak yaparsınız. Sonra case yapılarını vaka ifadelerini taklit etmek için kullanırsınız. Bir durum doğruysa derme çatma anahtar yapınızdan çıkmak için LABEL_NAME adlı mola kullanırsınız.

        DEFINE_TYPE:
        {
            if (a instanceof x){
                //do something
                break DEFINE_TYPE;
            }
            if (a instanceof y){
               //do something
                break DEFINE_TYPE;
            }
            if (a instanceof z){
                // do something
                break DEFINE_TYPE;
            }
        }

Bu OP tarafından verilen if... else ifkodundan nasıl daha iyi ?
daktilo

Sadece benim önceki yorumumu ayrıntılı olarak açıklamak için: önerdiğiniz şey aslında if... else ifgoto ifadeleriyle değiştirmektir . Bu, Java gibi dillerde kontrol akışını uygulamanın yanlış yoludur.
daktilo
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.