примеры - применение шаблонов java




Почему Java позволяет вам приводить в коллекцию? (2)

На этот вопрос уже есть ответ здесь:

У меня есть простой класс foo , и я могу привести к интерфейсу коллекции (либо Map либо List ) без каких-либо ошибок компилятора. Обратите внимание, что класс Foo не реализует какой-либо интерфейс или расширяет любой другой класс.

public class Foo {

    public List<String> getCollectionCast() {
        return (List<String>) this;    // No compiler error
    }

    public Map<String, String> getCollection2Cast() {
        return (Map<String, String>) this;    // No compiler error
    }

    public Other getCast() {
        return (Other)this;     // Incompatible types. Cannot cast Foo to Other
    }

    public  static class Other {
        // Just for casting demo
    }

}

Почему компилятор Java не возвращает ошибку несовместимых типов, когда я пытаюсь привести класс Foo к коллекции?

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


Компилятор не предотвращает приведение кода типом к интерфейсу, если только он не может точно установить, что связь невозможна.

Если целевой тип является интерфейсом, то это имеет смысл, потому что класс, расширяющий Foo может реализовать Map<String, String> . Однако обратите внимание, что это работает только потому, что Foo не является final . Если вы объявите свой класс с final class Foo , это приведение не будет работать.

Если целевой тип является классом, то в этом случае он просто потерпит неудачу (попробуйте (HashMap<String, String>) this ), потому что компилятор наверняка знает, что отношения между Foo и HashMap невозможны.

Для справки, эти правила описаны в JLS-5.5.1 (T = тип цели - Map<String, String> , S = тип источника - Foo )

Если T [целевой тип] является типом интерфейса:

  • Если S не является конечным классом (§8.1.1), то, если существует супертип X для T и супертип Y для S, такой, что X и Y являются доказуемо различными параметризованными типами и что стирание X и Y одинаковы, возникает ошибка времени компиляции.
    В противном случае приведение всегда допустимо во время компиляции (потому что даже если S не реализует T, подкласс S может).

  • Если S является конечным классом (§8.1.1), тогда S должен реализовать T, иначе произойдет ошибка времени компиляции.

Обратите внимание на комментарий, выделенный жирным курсивом в цитируемом тексте.


Это не потому, что они являются коллекционными классами, а потому, что они являются интерфейсами . Foo не реализует их, но подклассы этого могут. Так что это не ошибка времени компиляции, так как эти методы могут быть допустимы для подклассов. Во время выполнения , если this не класс, реализующий эти интерфейсы, естественно, это ошибка времени выполнения.

Если вы измените List<String> на ArrayList<String> , вы также получите ошибку времени компиляции, поскольку подкласс Foo может реализовывать List , но не может расширять ArrayList (так как Foo этого не делает). Точно так же, если вы сделаете Foo final , компилятор выдаст вам ошибку для приведения интерфейса, потому что он знает, что они никогда не могут быть истинными (поскольку Foo не может иметь подклассы и не реализует эти интерфейсы).





casting