8

I have following code:

 public class A {
    private String type;
    String getType() { return type;}
 }

Now in many code places I have code like this

 switch (a.geType()) {
  case "A" : return new Bla();
  case "B" : return new Cop();
 }

or somewhere else

switch (a.geType()) {
  case "A" : return new Coda();
  case "B" : return new Man();
 }

(Note that I know I should use an Enumeration in production code).

What I want to achive is that when a new type is added to class A the compiler should flag all the switch statements that need to be adjusted?

Is there a java idiomatic way to do this?

jack
  • 1,861
  • 4
  • 31
  • 52
  • 3
    If you use an enum, I'm pretty sure java does actually flag switch statements that don't handle that condition, so long as the switch doesn't have a `default` condition. – Pokechu22 Sep 16 '14 at 16:08
  • I just tried it out. It does not flag the missing enums out of the box – jack Sep 16 '14 at 16:16
  • 1
    I'm getting a warning in eclipse when I do it. It might not be part of the java compiler itself though. – Pokechu22 Sep 16 '14 at 16:20

4 Answers4

14

when a new type is added to class A the compiler should flag all the switch statements that need to be adjusted?

A good approach to this would be replacing switch statements with a more robust implementation of multiple dispatch, such as the Visitor Pattern:

interface VisitorOfA {
    Object visitA(A a);
    Object visitB(B b);
}
class A {
    Object accept(VisitorOfA visitor) {
        return visitor.visitA(this);
    }
}
class B extends A {
    Object accept(VisitorOfA visitor) {
        return visitor.visitB(this);
    }
}

With this infrastructure in place, you can remove your switch statements, replacing them with implementations of the visitor:

Object res = a.accept(new VisitorOfA() {
    public Object visitA(A a) { return new Bla(); }
    public Object visitB(B b) { return new Cop(); }
});

When you add a new subtype to A, say, class C, all you need to do is adding a new method to VisitorOfA:

Object visitC(C c);

Now the compiler will spot all places where this new method has not been implemented, helping you avoid problems at runtime.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    Somehow the switch statements look more lightweight and therefore more easier to understand for somebody reading the code. But I have to admit your answer solves the requirement "compiler flaging" – jack Sep 16 '14 at 16:23
  • A good improvement for this approach would be to store an instance of each implementacion into a Map. Moreover, you can load the map from a .properties file. – Little Santi Sep 16 '14 at 16:24
  • @jack I agree, visitor pattern looks tricky to someone looking at it for the first time. I am heavily biased here, because I've been using this pattern for many years. The advantage, however, is that one needs to learn this pattern only once to reliably recognize it in the future. It's like learning to ride a bicycle :) – Sergey Kalinichenko Sep 16 '14 at 16:30
  • The Visitor Pattern is a poor solution for class hierarchies that are expected to change. The GoF describe this cost/benefit of Visitor on page 333. "_Use the Visitor pattern when the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes._" – jaco0646 Nov 08 '18 at 20:33
8

Don't forget about good old-fashioned polymorphism. Having a "type" field with switch statements in a class is often a smell that indicates that subclassing might be useful. Consider:

public abstract class CommonSuperClass {
    public abstract One getOne();
    public abstract Two getTwo();
}

public class A extends CommonSuperClass {
    @Override public One getOne() { return new Bla(); }
    @Override public Two getTwo() { return new Coda(); }
}

public class B extends CommonSuperClass {
    @Override public One getOne() { return new Cop(); }
    @Override public Two getTwo() { return new Man(); }
}

If you were to add a new subclass C, you're required to provide implementations for the abstract methods (unless you make C itself be abstract).

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
6

You could have a map of string / suppliers:

Map<String, Supplier<Object>> map = new HAshMap<> ();
map.put("A", Bla::new);
map.put("B", Cop::new);

And your sample code would become:

return map.get(a.getType()).get(); //need null check
assylias
  • 321,522
  • 82
  • 660
  • 783
0

In perspective of abstraction, there is another approach for you to use. One way is via Polymorphism as shown here.

Some simple example:

public void EverythingYouWant (Animal animal) {
    return animal.move();
}

When it's more about refactoring replace type code/checking with State/Strategy patterns. It's good solution to first consider is there any reason that prevents subclassing.

Community
  • 1
  • 1
ekostadinov
  • 6,880
  • 3
  • 29
  • 47