Зачем разыгрывать после instanceOf? - PullRequest
22 голосов
/ 15 ноября 2010

В приведенном ниже примере (из моего пакета курсов) мы хотим дать Square экземпляру c1 ссылку на какой-то другой объект p1, но только если эти 2 имеют совместимые типы.

if (p1 instanceof Square) {c1 = (Square) p1;}

Что я не понимаю здесь, так это то, что мы сначала проверяем, что p1 действительно Square, а затем мы все еще разыгрываем его. Если это Square, зачем разыгрывать?

Я подозреваю, что ответ заключается в различии видимого и фактического типов, но, тем не менее, я запутался ...

Edit:
Как с этим справится компилятор:

if (p1 instanceof Square) {c1 = p1;}

Edit2:
Является ли проблема, которая instanceof проверяет тип фактическим , а не типом кажущегося ? И затем, что актерский состав меняет кажущийся тип?

Спасибо

JDelage

Ответы [ 10 ]

11 голосов
/ 15 ноября 2010

Имейте в виду, вы всегда можете назначить экземпляр Square типу выше по цепочке наследования.Затем вы можете привести менее конкретный тип к более конкретному типу, и в этом случае вы должны быть уверены, что ваш приведение действительно:

Object p1 = new Square();
Square c1;

if(p1 instanceof Square)
    c1 = (Square) p1;
10 голосов
/ 15 ноября 2010

Компилятор не делает вывод, что, поскольку вы находитесь в блоке, вы успешно проверили тип объекта.Явное приведение все еще требуется, чтобы сообщить компилятору, что вы хотите сослаться на объект как другой тип.

if (p1 instanceof Square) {
    // if we are in here, we (programmer) know it's an instance of Square
    // Here, we explicitly tell the compiler that p1 is a Square
    c1 = (Square) p1;
}

В C # вы можете выполнить проверку и вызов приведения в 1:

c1 = p1 as Square;

Это приведёт p1 к квадрату, а если произойдет сбой, c1 будет установлен на null.

5 голосов
/ 15 ноября 2010

Существует разница между измерением, поместит ли какой-либо объект в коробку, и фактическим помещением в коробкуinstanceof - первое, а кастинг - второе.

3 голосов
/ 09 августа 2015

Старый код не будет работать корректно

Возможность приведения impield в конце концов оправдана, но у нас есть проблемы с реализацией этого FR в java из-за обратной совместимости.

См. Это:

public class A {
    public static void draw(Square s){...} // with impield cast
    public static void draw(Object o){...} // without impield cast
    public static void main(String[] args) {
        final Object foo = new Square();
        if (foo instanceof Square) {
            draw(foo);
        }
    }
}

Текущий JDK скомпилирует использование второго объявленного метода.Если мы реализуем этот FR в Java, он скомпилируется с использованием первого метода!

3 голосов
/ 15 ноября 2010

Поскольку этот конкретный синтаксический сахар еще не добавлен в язык. Я думаю, что он был предложен для Java 7, но, похоже, он не вошел в монета проекта

1 голос
/ 15 ноября 2010

Переменная p1 имеет любой тип, с которого она начиналась - скажем, Shape. p1 - ​​это форма, и только форма, независимо от того, что ее текущее содержимое является квадратом. Вы можете вызвать, скажем, side () на квадрате, но не на Shape. До тех пор, пока вы идентифицируете рассматриваемый объект через переменную p1, тип которой - Shape, вы не можете вызывать side () из-за типа переменной. Как работает система типов Java: если вы можете вызвать p1.side (), когда узнаете, что это квадрат, вы можете всегда вызвать p1.side (). Но p1 может содержать не только квадратные формы, но и, скажем, Circle Shapes, и было бы ошибкой вызывать p1.side (), когда p1 содержит Circle. Таким образом, вам нужна другая переменная для представления Shape, которую вы знаете, это Square, переменная с типом Square. Вот почему актерский состав необходим.

1 голос
/ 15 ноября 2010

например. Если вы передадите p1 типа Object, компилятор не будет знать, что это на самом деле экземпляр Square, поэтому методы и т. Д. Не будут доступны. If просто проверяет, чтобы определенный тип возвращал true / false, но это не меняет тип переменной p1.

0 голосов
/ 15 ноября 2010

Не для того, чтобы быть противным, но вы должны сказать компилятору, что вы хотите сделать, потому что альтернативой было бы угадать, что вы пытаетесь сделать.Конечно, вы можете подумать: «Если я проверяю тип объекта, Ясно, что это должно означать, что я хочу привести его к этому типу».А кто говорит?Может быть, это то, что вы делаете, и, возможно, это не так.

Конечно, в простом случае, как

if (x instanceof Integer)
{
  Integer ix=(Integer) x;
  ...

Мои намерения довольно очевидны.Либо это?Возможно, я действительно хочу:

if (x instanceof Integer || x instanceof Double)
{
  Number n=(Number) x;
... work with n ...

Или что, если я напишу:

if (x instanceof Integer || x instanceof String)

Что вы ожидаете от компилятора дальше?Какой тип он должен принимать для x?

RE - комментарии о том, что instanceof устарел или иным образом плохая идея: он, безусловно, может быть использован неправильно.Недавно я работал над программой, в которой первоначальный автор создал шесть классов, которые, как оказалось, были страницами и страницами длиной, но идентичны друг другу, и единственной очевидной причиной их наличия было то, что он мог сказать «x instanceof classA» против »x instanceof classB "и т. д. То есть он использовал класс в качестве флага типа.Было бы лучше иметь только один класс и добавить перечисление для различных типов.Но есть также много очень хороших применений.Возможно, наиболее очевидным является что-то вроде:

public MyClass
{
  int foo;
  String bar;
  public boolean equals(Object othat)
  {
    if (!(othat instanceof MyClass))
      return false;
    MyClass that=(MyClass) othat;
    return this.foo==that.foo && this.bar.equals(that.bar); 
  }
  ... etc ...
}

Как бы вы сделали это без использования instanceof?Вы можете сделать параметр типа MyClass вместо Object.Но тогда нельзя будет даже назвать его с помощью универсального объекта, что может быть весьма желательно во многих случаях.Действительно, может быть, я хочу, чтобы коллекция включала, скажем, и строки, и целые числа, и я хочу, чтобы сравнения разных типов просто возвращали false.

0 голосов
/ 15 ноября 2010

Тест сделан для предотвращения ClassCastExceptions во время выполнения:

Square c1 = null;
if (p1 instanceof Square) {
   c1 = (Square) p1;
} else {
   // we have a p1 that is not a subclass of Square
}

Если вы абсолютно уверены, что p1 - это Square, тогда вам не нужно тестировать. Но оставь это частным методам ...

0 голосов
/ 15 ноября 2010

Если c1 объявлено как тип Square, тогда требуется приведение. Если он объявлен как Object, тогда приведение не требуется.

...