На самом деле, вы тут же сказали это достаточно хорошо.
Дело в том, что экземпляр comb почти всегда - плохая идея (исключение случается, например, когда вы маршалируете или сериализуете, когда в течение короткого интервала времени у вас может не быть под рукой всей информации о типе). Как говорит Джош, это в противном случае это признак плохой иерархии классов.
Вы знаете, что это плохая идея: это делает код хрупким: если вы его используете и иерархия типов изменится, то это, вероятно, нарушит этот экземпляр расчесывайте везде, где это встречается. Более того, вы теряете преимущества строгой типизации; компилятор не может помочь вам, заблаговременно обнаруживая ошибки. (Это в некоторой степени аналогично проблемам, вызванным приведением типов в C.)
Обновлять
Позвольте мне немного расширить это, поскольку из комментария кажется, что я не совсем понял. Причина, по которой вы используете приведение типов в C, или instanceof
, это то, что вы хотите сказать, как если бы: используйте это foo
, как если бы это было bar
. Теперь в C вообще нет информации о типе времени выполнения, поэтому вы просто работаете без сети: если вы укажете что-то, сгенерированный код будет обрабатывать этот адрес, как если бы он содержал определенный тип, независимо от того, что , и вам следует только надеяться, что это вызовет ошибку времени выполнения вместо того, чтобы что-то незаметно повредить.
Утиная печать просто поднимает это до нормы; в динамическом, слабо типизированном языке, таком как Ruby, Python или Smalltalk, все является нетипизированной ссылкой; вы стреляете в него сообщениями во время выполнения и смотрите, что происходит. Если он понимает конкретное сообщение, он ходит как утка - он его обрабатывает.
Это может быть очень удобно и полезно, потому что позволяет делать изумительные хаки, такие как присвоение выражения генератора переменной в Python или блока переменной в Smalltalk. Но это означает, что вы уязвимы для ошибок во время выполнения, которые строго типизированный язык может уловить во время компиляции.
В строго типизированном языке, таком как Java, строго говоря, у вас вообще не может быть утиной печати: вы должны указать компилятору, к какому типу вы собираетесь относиться. Вы можете получить что-то вроде утиного набора, используя приведение типов, так что вы можете делать что-то вроде
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]
Теперь во время выполнения все в порядке, пока x имеет вид FoodDispenser
в [1] или MissleController
в [2]; в противном случае бум. Или неожиданно нет бум.
В вашем описании вы защищаете себя гребешком из else if
и 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
Теперь вы защищены от ошибки времени выполнения, но обязаны сделать что-нибудь разумное позже, на else
.
Но теперь представьте, что вы вносите изменения в код, так что «x» может принимать типы «FloorWax» и «DessertTopping». Теперь вы должны пройти весь код, найти все экземпляры этого гребня и изменить их. Теперь код хрупкий - изменения требований означают много изменений кода. В объектно-ориентированном стиле вы стремитесь сделать код менее уязвимым.
Решение OO состоит в том, чтобы вместо этого использовать полиморфизм, который вы можете рассматривать как своего рода ограниченную типизацию утки: вы определяете все операции, выполнение которых можно доверять. Вы делаете это, определяя высший класс, возможно абстрактный, который имеет все методы низших классов. В Java такой класс лучше всего выражает интерфейс, но он имеет все свойства типа класса. Фактически, вы можете рассматривать интерфейс как обещание того, что конкретному классу можно доверять, чтобы он действовал так, как если бы это был другой класс.
public interface VeebleFeetzer { /* ... */ };
public class FoodDispenser implements VeebleFeetzer { /* ... */ }
public class MissleController implements VeebleFeetzer { /* ... */ }
public class FloorWax implements VeebleFeetzer { /* ... */ }
public class DessertTopping implements VeebleFeetzer { /* ... */ }
Все, что вам нужно сделать сейчас, это использовать ссылку на VeebleFeetzer, и компилятор разберется с этим за вас. Если вам случится добавить еще один класс, являющийся подтипом VeebleFeetzer, компилятор выберет метод и проверит аргументы в сделке.
VeebleFeetzer x; // A reference to anything
// that implements VeebleFeetzer
// Some code that assigns something to x
x.dropPellet();
// Some more code
x.launchAt("Moon");
person
Charlie Martin
schedule
31.03.2009