LINUX.ORG.RU

Java, массивы — почему здесь нет ошибки?

 , ,


0

4

Подскажите, почему такой вот кусок кода вообще может работать _без_ ошибок во время компиляции?

// ObjArr.java
  2 
  3 
  4 public class ObjArr {
  5     public static void main(String[] args) {
  6         Object[] objs = new Integer[20];
  7         objs[0] = "What the heck? It works o_O";
  8     }
  9 }

В это же время «строгая» Java не позволит мне сделать что-то наподобии

import java.util.*;

   public class ObjArr {
       public static void main(String[] args) {
          ArrayList<Object> objs = new ArrayList<Integer>();
      }
  }
 incompatible types
found   : java.util.ArrayList<java.lang.Integer>
required: java.util.ArrayList<java.lang.Object>
        ArrayList<Object> objs_1 = new ArrayList<Integer>();
                                   ^
1 error

Что-то я запутался...

Это весь бардак от того что эти части языка проектировались в разное время, массивы во времена 1.0, а генерики - 1.5

maxcom ★★★★★ ()

А что касается самого вопроса - массивы объектов в Java наследуются друг от друга, как объекты их типов. Т.е. Integer[] это child от Object[]. Это костыль, который придумали что-бы реализовать сортировку массивов и аналогичные методы один раз, а не для каждого отдельного типа массива.

maxcom ★★★★★ ()

Второе, вообще говоря, тоже должно было бы работать. Но не работает из-за не настолько умной реализации дженериков.

ArrayList<Object> objs = (ArrayList<Object>)(new ArrayList<Integer>());

Так должно скомпилироваться.

note173 ★★★★★ ()
Ответ на: комментарий от note173

Ну и заодно о типах:

Object obj = (String)((Object)(new Integer(1)));

Скомпилируется, но выдаст исключение во время выполнения, потому что приведение типа генерирует инструкцию checkcast, проверяющую совместимость. А вот в первом моем сообщении ошибки не будет, потому что для виртуальной машины ArrayList<Integer> ничем не отличается от ArrayList<MyClass>.

note173 ★★★★★ ()
Ответ на: комментарий от maxcom

При чём тут бардак, когда обычный боксинг? Любой Integer — наследник Object. И потому может присваиваться первому. Вот почему с дженериками иначе — это не особенно понятно.

KRoN73 ★★★★★ ()
Ответ на: комментарий от RedPossum

Ну ладно, тогда так:

(ArrayList<Object>)((Object)(new ArrayList<Integer>()));

note173 ★★★★★ ()
Ответ на: комментарий от KRoN73

потому что ковариантные массивы - не очень хорошо. собственно вот что по этому поводу говорит господин Одерски. Пусть у нас есть 3 типа. IntSet, NonEmptySet, EmptySet; иерархию. думаю объяснять не надо.

NonEmptySet[] a=new NonEmptySet[]{NonEmptySet(1)};
IntSet b=a;
b[0]=new EmptySet();
NonEmptySet errSet=a[0];

Error!!

RedPossum ★★★★★ ()
Ответ на: комментарий от RedPossum

IntSet b=a;

Так вот тут, как раз, и должна вылетать ошибка компиляции.

Боксинг подразумевает автоматическое приведение к базовому классу, но не наоборот.

KRoN73 ★★★★★ ()
Ответ на: комментарий от KRoN73

это не боксингом называется автоматическое приведение примитивного типа к reference типу. Т.е. int -> Int, double -> Double и т.д.

qnikst ★★★★★ ()

в первом случае у тебя обычный (не помню термина для не-generic) массив типа Object, инстансами которого, являются массивы любого ссылочного типа. Во втором случае у тебя generic массив типа Object, при этом не <? extends Object>, что должно скомпилироваться, а именно Object.

qnikst ★★★★★ ()
Ответ на: комментарий от note173

извращения какие

оно у тебя и так отработает

ArrayList<String> objs = (ArrayList<String>)((Object)(new ArrayList<Integer>()));

RedPossum ★★★★★ ()
Ответ на: комментарий от KRoN73

там IntSet[], прошу прощения. и тут именно что приведение к базому классу

RedPossum ★★★★★ ()
Ответ на: комментарий от KRoN73

тут обычная такая ситуация

class A; class B extends A;

B b = new B();

A a = b;

все же правильно?

RedPossum ★★★★★ ()
Ответ на: комментарий от RedPossum

даже не обращай на него внимания, это пхп-шный тролль-нытик, хотя он утверждает, что яву знает.

qnikst ★★★★★ ()
Ответ на: комментарий от RedPossum

Кстати, в это примере ведь вторая и третья строчка вообще работать не должны.

note173 ★★★★★ ()
Ответ на: комментарий от KRoN73

1) Это не боксинг 2) Это противоречит принципу подстановки, т.е. нарушает базовые один из базовых принципов ООП.

maxcom ★★★★★ ()
Ответ на: комментарий от KRoN73

Ну и следствие ковариантных массивов на Java в том, что очень легко создать ошибочную ситуацию, которую компилятор не определит, а в runtime все свалится с исключением о несовместимости типов. В современной Java это не проблема только по тому что с массивы обычно если и используются, то только в private полях объектов и не передаются напрямую

maxcom ★★★★★ ()
Ответ на: комментарий от note173

Работать или собираться? Вторая должна собраться, ковариантность жеж. еще раз повторюсь, во второй IntSet[].

RedPossum ★★★★★ ()
Ответ на: комментарий от KRoN73

Ковариантность корректна для readonly структур (и соответствующего кода в функциональном стиле). Для методов, изменяющих структуры, обычно работает контравариантность.

Массивы, как уже сказали, ковариантны только для удобства работы с ними в отсутствие дженериков и это не является правильным. Правильным было бы введение read-only алиаса для массива, который был бы ковариантным, но менять массив через него было бы нельзя, но так не сделали. Как делать правильно, можно посмотреть в скале.

Legioner ★★★★★ ()
Ответ на: комментарий от RedPossum

Собираться (если я правильно понял иерархию). Неявные widening преобразования разрешены, narrowing — нет.

note173 ★★★★★ ()
Ответ на: комментарий от Legioner

С мутабельными типами трудно корректно обеспечить вариантность

maxcom ★★★★★ ()
Ответ на: комментарий от note173

Все собралось и выкинула runtime exception

abstract class IntSet{
    abstract public void tell();
}

class NonEmptySet extends IntSet{
    private int val;
    private IntSet left;
    private IntSet right;

    NonEmptySet(int val, IntSet left, IntSet right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }

    @Override
    public void tell(){
        System.out.println("I'm not empty");
    }
}

class EmptySet extends IntSet{

    @Override
    public void tell(){
        System.out.println("I'm empty");
    }
}

public class Main {

    public static void main(String args[]) {
        NonEmptySet[] a=new NonEmptySet[]{new NonEmptySet(1,new EmptySet(),new EmptySet())};
        IntSet[] b=a;
        b[0]=new EmptySet();
        NonEmptySet errSet=a[0];
    }

}
RedPossum ★★★★★ ()
Ответ на: комментарий от RedPossum

Значит, я не понял иерархии. Понятнее было бы назвать EmptyIntSet и NonEmptyIntSet.

note173 ★★★★★ ()
Ответ на: комментарий от note173

Понятнее было бы назвать EmptyIntSet и NonEmptyIntSet.

это да.

RedPossum ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.