Apakah mungkin untuk menggunakan instance dari operator dalam pernyataan switch?

267

Saya memiliki pertanyaan tentang penggunaan sakelar untuk instanceofobjek:

Sebagai contoh: masalah saya dapat direproduksi di Jawa:

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

Bagaimana itu diterapkan menggunakan switch...case?

olidev
sumber
6
Jika Anda benar-benar merasa perlu beralih, Anda bisa mengubah nama kelas menjadi int dan menggunakannya, hati-hati terhadap kemungkinan bentrokan. Menambahkan sebagai komentar daripada jawaban karena saya tidak suka ide ini sebenarnya telah digunakan. Mungkin yang Anda butuhkan adalah pola pengunjung.
vickirk
1
Pada java 7 Anda bahkan dapat mengaktifkan nama kelas yang sepenuhnya memenuhi syarat untuk menghindari bentrokan hash seperti yang ditunjukkan oleh @vickirk, tetapi masih jelek.
Mitja
Mungkin dengan nama kelas sebagai nilai Enum
Murat Karagöz

Jawaban:

225

Ini adalah skenario khas di mana subtipe polimorfisme membantu. Lakukan hal berikut

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() } ... }

Maka Anda hanya dapat memanggil do()pada this.

Jika Anda tidak bebas untuk mengubah A, Bdan C, Anda bisa menerapkan pola pengunjung untuk mencapai yang sama.

jmg
sumber
33
Pola pengunjung berarti bahwa A, B, dan C harus mengimplementasikan antarmuka dengan metode abstrak yang mengambil Pengunjung sebagai parameter input, bagaimana jika Anda tidak dapat mengubah A, B, C dan tidak ada yang mengimplementasikan antarmuka itu?
thermz
21
Komentar terakhir tentang pola pengunjung salah. Anda masih perlu membuat A, B dan C mengimplementasikan antarmuka.
Ben Thurley
10
Sayangnya ini tidak berfungsi jika do () - Code membutuhkan lingkungan host (yaitu akses ke variabel yang tidak ada di do () itu sendiri).
mafu
2
Pertanyaan @mafu OP adalah tentang pengiriman berbasis tipe. Jika metode do () Anda membutuhkan lebih banyak input untuk mengirim daripada masalah Anda, IMHO di luar ruang lingkup pertanyaan yang dibahas di sini.
jmg
3
jawaban ini mengasumsikan bahwa Anda dapat memodifikasi kelas A, B, C, sementara saya pikir intinya adalah bagaimana melakukannya tanpa memodifikasi A, B, C karena mereka mungkin berada di perpustakaan bagian ketiga
cloudy_weather
96

jika Anda benar-benar tidak bisa membuat kode ke antarmuka, maka Anda bisa menggunakan enum sebagai perantara:

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;

}
Nico
sumber
thx, saya juga harus membuat beberapa perubahan: 1) menginisialisasi setiap enum id dengan referensi Kelas; 2) menegaskan nama sederhana kelas dengan enum id .toString (); 3) menemukan enum melalui referensi Kelas yang disimpan per id enum. Saya pikir ini juga aman kebingungan.
Aquarius Power
Jika this.getClass (). getSimpleName () tidak cocok dengan nilai CLAZZ, itu akan membuat Exception ... lebih baik dikelilingi dengan try catch block dan Exception akan diperlakukan sebagai opsi "default" atau "lain" dari saklar
tetri
40

Cukup buat Peta di mana kelas adalah kuncinya dan fungsinya, yaitu lambda atau sejenisnya, adalah nilainya.

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

// tentu saja, perbaiki ini hanya untuk menginisialisasi sekali

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

Jika Anda perlu memeriksa Pengecualian daripada mengimplementasikan FunctionalInterface yang melempar Pengecualian dan gunakan itu alih-alih Runnable.

Novaterata
sumber
3
Imho solusi terbaik, khususnya karena memungkinkan refactoring mudah.
Feiteira
2
Satu-satunya downside ke solusi ini adalah bahwa ia tidak dapat menggunakan variabel (metode) lokal di lambda (dengan asumsi itu tentu saja diperlukan).
zapatero
1
@zapatero Anda cukup mengubah ke Fungsi alih-alih Runnable, meneruskan instance dalam sebagai parameter, lalu
masukkan
Dipilih terbalik; ini adalah salah satu dari sedikit jawaban yang benar-benar membantu OP melakukan apa yang dia minta (dan ya, sering kali mungkin untuk refactor kode untuk tidak harus melakukan instanceof, dan tidak, skenario saya sayangnya bukan salah satu dari mereka yang mudah masuk ke dalam kotak itu ...)
Per Lundberg
@ SergioGutiérrez Terima kasih. Nah, pola ini seharusnya hanya diperlukan dengan perpustakaan eksternal. Dan meskipun begitu, Anda bisa membuat antarmuka dengan implementasi adaptor saja, tetapi itu berguna di mana Anda ingin DIFF perilaku, sehingga, untuk menjadi lebih jelas. Mirip dengan perutean API yang lancar vs anotasi, saya kira.
Novaterata
36

Untuk jaga-jaga jika seseorang akan membacanya:

Solusi TERBAIK di java adalah:

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

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

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

    };

    abstract void doAction (...);
}

Manfaat BESAR dari pola tersebut adalah:

  1. Anda cukup melakukannya (TIDAK ADA saklar sama sekali):

    void someFunction ( Action action ) {
        action.doAction(...);   
    }
  2. Jika Anda menambahkan Aksi baru yang disebut "d", Anda HARUS menerapkan metode doAction (...)

CATATAN: Pola ini dijelaskan dalam Joshua's Bloch "Java Efektif (Edisi 2)"

se.solovyev
sumber
1
bagus! Apakah @Overridediperlukan di atas setiap implementasi doAction()?
mateuscb
9
Bagaimana ini solusi "TERBAIK"? Bagaimana Anda memutuskan mana yang actionakan digunakan? Dengan contoh luar kaskade yang memanggil someFunction()dengan yang benar action? Ini hanya menambah tingkat tipuan lainnya.
PureSpider
1
Tidak, itu akan dilakukan secara otomatis saat runtime. Jika Anda memanggil someFunction (Action.a) maka a.doAction akan dipanggil.
se.solovyev
11
Saya tidak mengerti ini. Bagaimana Anda tahu enum mana yang akan digunakan? Seperti yang dikatakan @PureSpider, ini sepertinya hanya pekerjaan lain yang harus dilakukan.
James Manes
2
Sangat menyedihkan bahwa Anda tidak menawarkan contoh lengkap , misalnya bagaimana memetakan Kelas-instance dari a, b atau C ke enum ini. Saya akan mencoba untuk memberikan Instance ke Enum ini.
Tom
21

Kamu tidak bisa The switchpernyataan hanya dapat berisi casepernyataan yang konstanta waktu kompilasi dan yang mengevaluasi ke integer (Sampai dengan Java 6 dan string di Jawa 7).

Apa yang Anda cari disebut "pencocokan pola" dalam pemrograman fungsional.

Lihat juga Menghindari contoh di Jawa

Carlo V. Dango
sumber
1
Tidak, di sebagian besar bahasa fungsional Anda tidak dapat mencocokkan pola pada jenis, hanya pada konstruktor. Setidaknya itu benar dalam ML dan Haskell. Dalam Scala dan OCaml dimungkinkan tetapi bukan aplikasi khas pencocokan pola.
jmg
Tentu, tetapi memeriksa terhadap konstruktor akan "setara" dengan skenario yang dijelaskan di atas.
Carlo V. Dango
1
Dalam beberapa kasus, tetapi tidak secara umum.
jmg
Switch juga dapat mendukung enum.
Solomon Ucko
"Kamu tidak bisa" melihat bahasa yang berbeda jarang merupakan jawaban yang bermanfaat.
L. Blanc
17

Sebagaimana dibahas dalam jawaban teratas, pendekatan OOP tradisional adalah menggunakan polimorfisme alih-alih beralih. Bahkan ada pola refactoring yang terdokumentasi dengan baik untuk trik ini: Ganti Kondisional dengan Polimorfisme . Setiap kali saya meraih pendekatan ini, saya juga ingin mengimplementasikan objek Null untuk memberikan perilaku default.

Dimulai dengan Java 8, kita bisa menggunakan lambdas dan generik untuk memberi kita sesuatu yang programmer sangat akrab dengan: pencocokan pola. Ini bukan fitur bahasa inti tetapi perpustakaan Javaslang menyediakan satu implementasi. Contoh dari javadoc :

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

Ini bukan paradigma paling alami di dunia Jawa jadi gunakan dengan hati-hati. Sementara metode generik akan menyelamatkan Anda dari keharusan mengetikkan nilai yang cocok, kami kehilangan cara standar untuk menguraikan objek yang cocok seperti dengan kelas kasus Scala misalnya.

Pavel
sumber
9

Saya tahu ini sangat terlambat tetapi bagi pembaca di masa depan ...

Waspadalah terhadap pendekatan di atas yang hanya didasarkan pada nama kelas A , B , C ...:

Kecuali jika Anda dapat menjamin bahwa A , B , C ... (semua subclass atau pelaksana Basis ) final, maka subclass dari A , B , C ... tidak akan ditangani.

Meskipun pendekatan if, elseif, elseif .. lebih lambat untuk sejumlah besar subclass / pelaksana, itu lebih akurat.

JohnK
sumber
Memang, Anda tidak boleh menggunakan polimorfim (alias OOP)
Val
8

Sayangnya, tidak mungkin keluar dari kotak karena pernyataan switch-case mengharapkan ekspresi yang konstan. Untuk mengatasinya, salah satu caranya adalah dengan menggunakan nilai enum dengan nama kelas mis

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);
 }
}

Dengan itu dimungkinkan untuk menggunakan pernyataan switch seperti ini

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
}
Murat Karagöz
sumber
Saya percaya bahwa sampai masalah JEP 8213076 sepenuhnya diimplementasikan, ini adalah cara terbersih mendapatkan pernyataan switch berdasarkan jenisnya, tanpa memodifikasi kelas target.
Rik Schaaf
5

Tidak, tidak ada cara untuk melakukan ini. Namun yang mungkin ingin Anda lakukan adalah mempertimbangkan Polimorfisme sebagai cara untuk menangani masalah semacam ini.

Andreas Johansson
sumber
5

Menggunakan pernyataan switch seperti ini bukan cara berorientasi objek. Anda sebaiknya menggunakan kekuatan polimorfisme . Cukup tulis

this.do()

Setelah sebelumnya membuat kelas dasar:

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

yang merupakan kelas dasar untuk A, Bdan 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() }
}
Raedwald
sumber
@jmg suggeests ( stackoverflow.com/questions/5579309/switch-instanceof/… ) menggunakan antarmuka bukan kelas dasar abstrak. Itu bisa lebih unggul dalam beberapa sirkulasi.
Raedwald
5

Ini akan bekerja lebih cepat dan masuk akal jika
- Anda memiliki banyak 'kasus'
- proses dieksekusi dalam konteks sensitif kinerja

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());
    }
}
Mike
sumber
1
Ini tidak sama dengan melakukan instanceof, karena ini hanya berfungsi jika kelas implementasi digunakan untuk beralih, tetapi tidak akan berfungsi untuk antarmuka / abstractclass / superclasses
lifeso ordinary
ya, ini bukan
Mike
A for effort, tetapi selain dari komentar @ lifesoordinary, Anda juga kehilangan ketelitian jenis yang biasanya Anda miliki, karena jawaban ini menggunakan string hardcoded, bukan referensi kelas. Hal ini sangat mudah untuk membuat kesalahan ketik, terutama jika Anda akan perlu untuk memperluas fungsi ini dengan nama kanonik penuh jika sini adalah tumpang tindih dalam nama-nama kelas dengan paket yang berbeda names.Edit: typo tetap (yang agak membuktikan poin saya)
Rik Schaaf
4

Anda tidak dapat beralih hanya bekerja dengan byte, pendek, char, int, String dan jenis enumerated (dan versi objek primitif, itu juga tergantung pada versi java Anda, String dapat switchdiedit di dalam java 7)

Tnem
sumber
Anda tidak dapat mengaktifkan Strings di Java 6. Dan Anda tidak dapat mengaktifkan "versi objek primitif".
Lukas Eder
@ Bozho Saya memang mengatakan itu tergantung pada versi java Anda, di Java 7 Anda dapat mengaktifkan Strings.
Tnem
@Lukas Eder periksa spec java Anda
Tnem
4

Saya pribadi suka kode Java 1.8 berikut:

    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");
            });

Akan menghasilkan:

YYyy

Kode sampel menggunakan Strings tetapi Anda dapat menggunakan jenis objek apa pun, termasuk Kelas. misalnya.myCase(this.getClass(), (o) -> ...

Perlu cuplikan berikut:

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);
}
Feiteira
sumber
4

Java sekarang memungkinkan Anda untuk beralih dengan cara OP. Mereka menyebutnya Pencocokan Pola untuk sakelar. Saat ini sedang dalam Rancangan tetapi melihat berapa banyak pekerjaan yang telah mereka masukkan ke switch baru-baru ini saya pikir itu akan melalui. Contoh yang diberikan dalam JEP adalah

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();
}  

atau menggunakan sintaks lambda mereka dan mengembalikan nilai

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();
    };

bagaimanapun mereka telah melakukan hal-hal keren dengan sakelar.

A_Arnold
sumber
3

Jika Anda dapat memanipulasi antarmuka umum, Anda bisa menambahkan enum dan meminta setiap kelas mengembalikan nilai unik. Anda tidak memerlukan instanceof atau pola pengunjung.

Bagi saya, logika perlu ditulis dalam pernyataan switch, bukan objek itu sendiri. Ini solusi saya:

ClassA, ClassB, and ClassC implement CommonClass

Antarmuka:

public interface CommonClass {
   MyEnum getEnumType();
}

Enum:

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;
  }

Implan:

...
  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;
  }
...

Jika Anda menggunakan java 7, Anda bisa meletakkan nilai string untuk enum dan blok kasus sakelar akan tetap berfungsi.

Gaʀʀʏ
sumber
The valuelapangan berlebihan jika Anda hanya ingin membedakan konstanta enum - Anda dapat menggunakan konstanta secara langsung (seperti yang Anda lakukan).
user905686
2

Bagaimana dengan ini ?

switch (this.name) 
{
  case "A":
    doA();
    break;
  case "B":
    doB();
    break;
  case "C":
    doC();
    break;
  default:
    console.log('Undefined instance');
}
Joeri
sumber
3
Harus menunjukkan bahwa ini hanya berfungsi di Java 7. Dan Anda harus menelepon this.getSimpleName()Tidak yakin apakah poster itu bingung dengan JS (ya, dia menggunakan konsol, hehe).
pablisco
5
Ini memiliki masalah tidak adanya transparansi referensi kode sumber. Artinya, IDE Anda tidak akan dapat mempertahankan integritas referensi. Misalkan Anda ingin mengganti nama nama Anda. Refleksi itu jahat.
Val
1
Bukan ide yang bagus. Nama kelas tidak unik jika Anda memiliki beberapa pemuat kelas.
Doradus 3-15
Istirahat ketika datang ke kompresi kode (→ ProGuard)
Matthias Ronge
1

Saya pikir ada alasan untuk menggunakan pernyataan switch. Jika Anda menggunakan xText, kode yang dihasilkan mungkin. Atau jenis lain dari kelas yang dihasilkan EMF.

instance.getClass().getName();

mengembalikan sebuah String dari Nama Implementasi Kelas. yaitu: org.eclipse.emf.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

mengembalikan representasi sederhana yaitu: EcoreUtil

Thomas Haarhoff
sumber
Anda tidak dapat menggunakannya dalam switchsebagai casekondisi karena tidak nilai konstan
B-gangster
1

Jika Anda perlu "beralih" melalui tipe objek "ini" kelas ini, jawaban ini adalah yang terbaik https://stackoverflow.com/a/5579385/2078368

Tetapi jika Anda perlu menerapkan "beralih" ke variabel lain. Saya akan menyarankan solusi lain. Tetapkan antarmuka berikut:

public interface ClassTypeInterface {
    public String getType();
}

Terapkan antarmuka ini di setiap kelas yang Anda ingin "beralih". Contoh:

public class A extends Something implements ClassTypeInterface {

    public final static String TYPE = "A";

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

Setelah itu Anda bisa menggunakannya dengan cara berikut:

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

Satu-satunya hal yang harus Anda perhatikan - pertahankan "tipe" unik di semua kelas yang mengimplementasikan ClassTypeInterface. Ini bukan masalah besar, karena jika ada persimpangan Anda menerima kesalahan waktu kompilasi untuk pernyataan "sakelar kasus".

Sergey Krivenkov
sumber
Alih-alih menggunakan String untuk TYPE, Anda dapat menggunakan enum dan keunikan dijamin (seperti yang dilakukan dalam jawaban ini ). Namun, dengan salah satu pendekatan yang Anda harus refactor di dua tempat ketika Anda mengganti nama.
user905686
@ user905686 ganti nama apa? Dalam contoh saat ini, tipe "A" didefinisikan di dalam kelas Sesuatu untuk meminimalkan jumlah kode. Tetapi dalam kehidupan nyata Anda harus mendefinisikannya di luar (di beberapa tempat umum) dan tidak ada masalah dengan refactoring lebih lanjut.
Sergey Krivenkov
Maksud saya mengubah nama kelas A. Refactoring otomatis mungkin tidak termasuk variabel TYPE = "A"saat penggantian nama. Terutama jika itu di luar kelas yang sesuai, orang mungkin juga lupa ketika melakukannya secara manual. IntelliJ sebenarnya juga menemukan kemunculan nama kelas dalam string atau komentar tetapi itu hanya pencarian teks (alih-alih melihat pohon sintaks) dan dengan demikian menyertakan false positive.
user905686
@ user905686 itu hanya sebuah contoh, untuk memvisualisasikan idenya. Jangan gunakan String untuk tipe definisi dalam proyek nyata, mendeklarasikan beberapa pemegang kelas MyTypes dengan konstanta integer (atau enum) dan menggunakannya dalam kelas yang mengimplementasikan ClassTypeInterface.
Sergey Krivenkov
1

Berikut cara fungsional untuk menyelesaikannya di Java 8 menggunakan http://www.vavr.io/

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)
        );
    }
Viswanath
sumber
1

Meskipun tidak mungkin untuk menulis pernyataan switch, dimungkinkan untuk bercabang ke pemrosesan spesifik untuk setiap jenis yang diberikan. Salah satu cara untuk melakukan ini adalah dengan menggunakan mekanisme pengiriman ganda standar. Contoh di mana kita ingin "beralih" berdasarkan tipe adalah Jersey Exception mapper di mana kita perlu memetakan banyak pengecualian untuk respons kesalahan. Sementara untuk kasus khusus ini mungkin ada cara yang lebih baik (yaitu menggunakan metode polimorfik yang menerjemahkan setiap pengecualian ke respons kesalahan), menggunakan mekanisme pengiriman ganda masih berguna dan praktis.

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);
    }
}

Maka di mana pun "saklar" dibutuhkan, Anda dapat melakukannya sebagai berikut:

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";
    }
}
Ravi Sanwal
sumber
Ini sangat mirip dengan pola Pengunjung, yang sudah dibahas dalam jawaban ini: stackoverflow.com/a/5579385
typeracer
0

Buat Enum dengan nama Kelas.

public enum ClassNameEnum {
    A, B, C
}

Temukan nama kelas objek. Tulis kotak saklar di atas enum.

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;
        }
    }
}

Semoga ini membantu.

Siva Kumar
sumber
2
Berbeda dengan pendekatan ini di mana kopling antara konstanta enum dan kelas dilakukan secara eksplisit, Anda melakukan kopling secara implisit dengan nama kelas. Ini akan memecah kode Anda ketika Anda hanya mengganti nama salah satu konstanta enum atau kelas sedangkan pendekatan lainnya masih bekerja.
user905686
0

Kerangka Pemodelan Eclipse memiliki ide menarik yang juga mempertimbangkan warisan. Konsep dasar didefinisikan dalam antarmuka Switch : switching dilakukan dengan memanggil metode doSwitch .

Yang benar-benar menarik adalah implementasinya. Untuk setiap jenis bunga, a

public T caseXXXX(XXXX object);

Metode harus diimplementasikan (dengan implementasi standar mengembalikan nol). The doSwitch implementasi akan mencoba untuk memanggil al caseXXX metode pada objek untuk semua jenis hirarki-nya. Sesuatu di baris:

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;

Kerangka kerja yang sebenarnya menggunakan id integer untuk setiap kelas, jadi logikanya sebenarnya adalah saklar murni:

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:
    {
      ...
    }
  ...

Anda dapat melihat implementasi lengkap ECoreSwitch untuk mendapatkan ide yang lebih baik.

Arcanefoam
sumber
-1

bahkan ada cara yang lebih sederhana untuk meniru struktur saklar yang menggunakan instanceof, Anda melakukan ini dengan membuat blok kode dalam metode Anda dan menamainya dengan label. Kemudian Anda menggunakan struktur jika untuk meniru pernyataan kasus. Jika kasing benar maka Anda menggunakan break LABEL_NAME untuk keluar dari struktur sakelar sementara Anda.

        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;
            }
        }
Maurice
sumber
Bagaimana ini lebih baik daripada if... else ifkode yang diberikan oleh OP?
typeracer
Hanya untuk menguraikan komentar saya sebelumnya: apa yang Anda usulkan pada dasarnya adalah untuk menggantikan if... else ifdengan pernyataan "goto", yang merupakan cara yang salah untuk menerapkan aliran kontrol dalam bahasa seperti Java.
typeracer