Is it possible to avoid using type checking in this example?

Sorry for the bad title, can’t come up with a concise way to put this.

I am thinking of having a list of objects that will have a specific interface. Each of these objects can then implement additional interfaces, but there is no guarantee which object will implement it. However, in one loop, I want to be able to call methods of any of their additional subtypes.

That is, 3 interfaces:

public interface IAnimal { ... }
public interface IEggLayer { public Egg layEgg(); }
public interface IMammal { public void sweat(); }

it will then be saved as

private List<IAnimal> animals= new ArrayList<IAnimal>();

therefore, instances added to the list can also be of type IEggLayeror IMammal, which have completely unrelated methods.

My initial instinct would then be to do

for(IAnimal animal : animals) {
  if(animal instanceof IEggLayer) {
    egg = ((IEggLayer)animal).layEgg();
  }
  if(animal instance of IMammal) {
    ((IMammal)animal).sweat();
  }
}

But I was always told that type checking is a sign that the code really needs to be reorganized.

[, ], , doFunction() , , ?
, ?

...
[ , - Java-]

lvalue EggLayer, ,

+5
6

, - , .

, , , , , . /, .

, , , , , , .

, . isEggLayer()/isMammal() ;

if (x.isEggLayer()) {
    ((IEggLayer) x).layEgg();  // type cast is required.
}

, asEggLayer(); .

public IEggLayer asEggLayer() {
    return ((IEggLayer) this);
}

// Not recommended ...
public IEggLayer asEggLayer() {
    return (this instanceof IEggLayer) ? ((IEggLayer) this) : null;
}

typecheck, , . , "" , , .

+1

, IAnimal ( ) callAllMethods, - OO-sound!

+5

#, .

foreach(IEggLayer egglayer in animals) {
    egglayer.layEgg();
}

foreach(IMammal mammal in animals) {
    mammal.sweat();
}
+1

, : ? - . - IAnimal, .

, , , . , null; . , generics:

  public interface IAnimal<R> {

         public R generalMethod();

  }

  public interface IEggLayer extends IAnimal<Egg> {

         public Egg generalMethod(); //not necessary, but the point is it works.

  }

  public interface IMammal extends IAnimal<Void> {

        public Void generalMethod();

  }

, , factory, - , .

+1

isAnimal:

public interface IAnimal {
   bool isEggLayer();
   bool isMammal();
}

.

Update: , , , , drawVehicle, , cessna, , .

, , -OOP-, , , , , , AOP .

,

@IsEggLayer
@IsMammal
public class Platypus() implements EggLayer, Mammal {
...
}

, .

, .

, , , , .

0

. Exaclty, , . . . , ( , ).

, layEggsOrSweatOrDoBothIfYoureWeirdOrNoneIfYouCant() - polute Animal . , Animal animals, . "" - , , .

private final List<AnimalWrapper> animals =
    new ArrayList<AnimalWrapper>();

public void doStuff() {
    for (AnimalWrapper animal : animals) {
        animal.doStuff();
    }
}

- . - :

public void addPlatypus(final Platypus platypus) {
    animals.add(new AnimalWrapper() { public void doYourStuff() {
        platypus.sweat();
        platypus.layEgg();
    }});
}

If you try to write these wrappers without enough context, you get into trouble. This requires that the correct site is selected on the call site. This can be done by overload, but it has dangers.

/*** Poor context -> trouble ***/

public void addNormalMamal(final Mamal mamal) {
    animals.add(new AnimalWrapper() { public void doYourStuff() {
        mamal.sweat();
    }});
}
public void addNormalEggLayer(final EggLayer eggLayer) {
    animals.add(new AnimalWrapper() { public void doYourStuff() {
        eggLayer.layEgg();
    }});
}
public <T extends Mamal & EggLayer> void addMamalEggLayer(final T animal) {
    animals.add(new AnimalWrapper() { public void doYourStuff() {
        animal.sweat();
        animal.layEgg();
    }});
}
0
source

All Articles