How can I argue with duck-typing in a strongly typed language like Java?

I am working on a team of Java programmers. One of my colleagues occasionally suggests that I do something like "just add a field of type" (usu. "String type"). Or the code will be passed with " if (foo instanceof Foo){...} else if( foo instanceof Bar){...} ".

Josh Bloch warns that “tagged classes are an imitation of the correct class hierarchy”, despite the fact that my one-line answer to such things? And then, as I develop the concept more seriously?

It’s clear to me that - the Java context is the type of the object in question right in front of our collective individuals - IOW: the word immediately after "class", "enumeration" or "interface", etc.

But besides being difficult to demonstrate or quantify (in place) “it makes your code more complex”, how can I say that “duck printing in a more or less strongly typed language is a stupid idea that involves a much deeper design pathology?

+6
java design duck-typing
source share
11 answers

When you say "duck, typing in highly typed languages," you really mean "imitating (subtype) polymorphism in statically typed languages."

This is not so bad when you have data objects (DTOs) that do not contain any behavior. When you have a full-blown OO model (ask yourself if this is true), you should use the polymorphism offered by the language where necessary.

+1
source share

In fact, you said it was good enough.

The truth is that the “instance” of the comb almost always represents a bad idea (an exception occurs, for example, when marshaling or serializing, when for a short period of time you may not have all the type information at hand.) As Josh says, this sign of a bad class hierarchy otherwise.

What you know is a bad idea, that it makes the code fragile: if you use it and the type hierarchy changes, then it probably breaks this crest instance in all its place. What more, you lose the benefits of strong typing; the compiler cannot help you by catching errors ahead of time. (This is somewhat similar to the problems caused by types in C.)

Update

Let me expand this a bit, as from the comment it seems like I'm not entirely clear. The reason you use a type in C, or instanceof , is because you want to say "as if": use this foo , as if it were a bar . Now in C there is no information about the type of runtime, so you just work without a network: if you write something, the generated code will treat this address as if it contained a certain type, no matter what, and you should just hoping this will lead to a runtime error, rather than quietly corrupting something.

Duck seal only raises this rate; in a dynamic, weakly typed language like Ruby or Python or Smalltalk, this is all an untyped link; you capture messages at runtime and see what happens. If he understands a specific message, he "walks like a duck" - he processes it.

This can be very convenient and useful because it allows amazing hacks to assign a generator expression to a variable in Python or a variable block in Smalltalk. But this means that you are vulnerable to run-time errors that can be fixed at compile time by a strongly typed language.

In a strongly typed language such as Java, you cannot, strictly speaking, have duck print: you must tell the compiler which type you are going to process something. You can get something like duck print using type casting so you can do something like

 Object x; // A reference to an Object, analogous to a void * in C // Some code that assigns something to x ((FoodDispenser)x).dropPellet(); // [1] // Some more code ((MissleController)x).launchAt("Moon"); // [2] 

Now at runtime, you are fine as long as x is a kind of FoodDispenser in [1] or MissleController in [2]; otherwise an arrow. Or unexpectedly, without a boom.

In your description, you protect yourself by combing else if and instanceof

  Object x ; // code code code if(x instanceof FoodDispenser) ((FoodDispenser)x).dropPellet(); else if (x instanceof MissleController ) ((MissleController)x).launchAt("Moon"); else if ( /* something else...*/ ) // ... else // error 

You are now protected from runtime errors, but you are responsible for doing something reasonable later on else .

But now imagine that you are making changes to the code so that "x" can accept the types "FloorWax" and "DessertTopping". Now you have to go through all the code and find all instances of this comb and change them. Now the code is fragile - changes in requirements mean a lot of code changes. In OO, you are trying to make the code less fragile.

OO's solution is to use polymorphism instead, which you can think of as a type of limited set of ducks: you define all the operations you can trust. You do this by defining an excellent class, possibly abstract, that has all the methods of the lower classes. In Java, such a class is best expressed by an "interface", but it has all the properties of a class type. In fact, you can see the interface as a promise that you can trust a particular class to act as if it were another class.

  public interface VeebleFeetzer { /* ... */ }; public class FoodDispenser implements VeebleFeetzer { /* ... */ } public class MissleController implements VeebleFeetzer { /* ... */ } public class FloorWax implements VeebleFeetzer { /* ... */ } public class DessertTopping implements VeebleFeetzer { /* ... */ } 

All you have to do is use the link to VeebleFeetzer, and the compiler will explain it to you. If you add another class that is a subtype of VeebleFeetzer, the compiler will select this method and check the arguments in the deal

 VeebleFeetzer x; // A reference to anything // that implements VeebleFeetzer // Some code that assigns something to x x.dropPellet(); // Some more code x.launchAt("Moon"); 
+8
source share

This is not so much a duck typing as an object oriented style; indeed, having the ability to subclass class A and call the same method in class B, and do something else, this is a whole point of inheritance in languages.

If you constantly check the type of object, then you are either too smart (although I believe that this is a trick that duck lovers love, except in a less fragile form), or you do not embrace the basics of object-oriented programming.

+6
source share

Mmm ...

correct me if I am wrong, but marked classes and duck print are two different concepts, although they are not necessarily exclusive.

If someone wants to use tags in a class to determine the type, then IMHO should rethink their hiearchy class, as this is a clear song of conceptual bleeding, where the abstract class needs to know the implementation details that class parenting is trying to hide. Are you using the correct template? In other words, are you trying to force behavior in a template that naturally does not support it?

If, as duck input, the ability to freely determine the type in which the method can accept any types is used only if the necessary methods are defined in the parameter instance. Then the method will use this parameter and invoke the necessary methods without worrying too much about the parent instance of the instance.

So, here ... a smelly hint, as Charlie remarked, using an instance. Like static or other smelly keywords, each time they appear, you need to ask: “Am I doing the right thing here?”, Not that they are inherently wrong, but they are often used to crack bad or poorly adapted OO desing.

+3
source share

My answer to one answer will be that you lose one of the main benefits of OOP: polymorphism. This shortens the development time for new code (developers like to develop new code, so this should help your argument :-)

If, when adding a new type to an existing system, you need to add logic, in addition to figuring out which instance to build, then in Java you are doing something wrong (assuming that the new class should just be replaced by another).

Generally, the appropriate way to handle this in Java is to keep the code polymorphic and use interfaces. Therefore, at any time they may want to add another variable or make an instance, perhaps they should implement the interface.

If you can convince them to change the code, it is fairly easy to modify the interfaces into an existing code base. In this regard, I would take the time to take a piece of code with an instance and reorganize it as polymorphic. People are much easier to see if they can see before and after the versions and compare them.

+2
source share

You can point your employee to the Liskov replacement principle, one of the five pillars in SOLID.

References:

+2
source share

Although I'm generally a fan of languages ​​with a duck language, like python, I see your problem with it in java.

If you write all the classes that will ever be used with this code, you do not need to specify the type of duck, because you do not need to allow cases where the code cannot directly inherit (or implement) an interface or other unifying abstraction.

The disadvantage of duck dialing is that you have an extra class of unit tests to run on your code: the new class may return a different type than expected and subsequently crash the rest of the code. Therefore, although duck typing provides flexibility in the opposite direction, testing requires a lot of advanced thinking.

In short, you have a trick (hard) and not a trap (easy). I think pathology.

+1
source share

Why "simulate a class hierarchy" instead of constructing and using it? One refactoring method replaces “switch” es (chained ifs are almost the same) with polymorphism. Why use switches where polymorphism leads to cleaner code?

+1
source share

This is not a duck, it's just a bad way to model polymorphism in a language that has (more or less) real polymorphism.

+1
source share

Two arguments to answer the named question:

1) It is assumed that Java will "write once, work anywhere", so code written for one hierarchy should not throw RuntimeExceptions when we change the environment somewhere. (Of course, there are exceptions - pun intended to this rule.)

2) Java JIT performs very aggressive optimizations that rely on the knowledge that a given character must be of the same type and one type. The only way around this is to quit.

As already mentioned, your “instance” does not match the question I answered here. Anything with any type, duck, or static can have the problem described. There are better OOP ways to handle this.

0
source share

Instead of an instance, you can use the Method- and Strategy-Pattern mixed together, the code looks much better than before ...

0
source share

All Articles