Рекурсивный тип bound - semantic [duplicate]

Вам нужно будет сделать этот сервер. Если вы используете PHP, посмотрите на glob .

Например:

foreach (glob("path/to/css/*.css") as $css) {
    echo "\n";
}

8
задан Kinga Odecka 18 August 2014 в 16:24
поделиться

4 ответа

Отличие состоит в том, что Some<E extends Some> означает две вещи:

  • E является сырым типом. Вкратце, типы raw имеют всю общую информацию, удаляемую из класса, что, в свою очередь, может создать неожиданное поведение
  • E, может быть любым классом Some!

Наоборот, использование Some<E extends Some<E>> означает:

  • E напечатан
  • E должен быть тем же как класс, в котором он объявлен

Необработанная часть - проблема, но большая импликация - это границы типов. Этот код демонстрирует разницу, поскольку классы «B» используют (или пытаются использовать) классы «A» как общий тип.

// The raw version
interface Some<E extends Some> {
    E get();
}

class SomeA implements Some<SomeA> {
    public  SomeA get() {
         return new SomeA();
    }
}

// Compiles OK
class SomeB implements Some<SomeA> {
   public SomeA get() {
         return new SomeA();
    }
}

// The typed version
interface SomeT<E extends SomeT<E>> {
    E get();
}

class SomeTA implements Some<SomeTA> {
    public  SomeTA get() {
         return new SomeTA();
    }
}

// Compile error 
class SomeTB implements SomeT<SomeA> {
   public SomeA get() { 
         return new SomeTA();
   }
}

Класс SomeB компилирует в порядке - тип E привязан только к Some, поэтому будет выполняться любой класс Some, но класс SomeTB выдает ошибку компиляции:

аргумент типа SomeTA не входит в число границы переменной типа E

Тип E должен быть точно таким же, как содержащий класс.

Это важно, если вы ожидаете параметров и возвращаемые типы должны быть такими же, как и сам класс, который обычно вы хотите использовать с типизированными классами.


Тип, ограниченный сырым типом, не является проблемой, поскольку он все еще является Some, но могут быть случаи, когда это имеет значение (я не могу думать об этом только сейчас).

1
ответ дан Bohemian 26 August 2018 в 12:45
поделиться

Что касается оператора

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

Это не должно быть дело. Он должен печатать предупреждение, потому что вы используете raw type Some, и в результате этого отсутствует безопасность типа, как показано в ответе на newacct .


Пример Dog / Cat немного надуман и несколько испорчен. Вы предложили объявить класс

public class Dog extends Animal<Cat> { } // Note "Cat" !!!

. Но здесь параметр типа в основном означает: «Этот параметр (Cat) является типом, который объекты этого класса (Dog) могут быть по сравнению с ". Таким образом, вы явно заявляете , что Dog должен быть сопоставим с Cat. В конце концов, даже разработчикам сложного языка и умных компиляторов приходится программировать код, который имеет смысл.


Действительно, не так много случаев, когда эти самореферентные общие типы необходимо. Один из этих примеров нарисован в этой записи в FAQ: Он объявляет структуру узлов (то есть дерево), где параметр типа может использоваться для отграничения определения структуры дерева / g5] из фактического типа узлов:

public class RecurringTest {
    public static void main(String[] args) {
        SpecialNode sa = new SpecialNode(null);
        SpecialNode sb = new SpecialNode(sa);
        SpecialNode s = sa.getChildren().get(0);
    }
}

abstract class Node<N extends Node<N>> {
    private final List<N> children = new ArrayList<N>();
    private final N parent;

    protected Node(N parent) {
        this.parent = parent;
        if (parent != null) {
            this.parent.getChildren().add(getThis());
        }
    }

    abstract N getThis();

    public N getParent() {
        return parent;
    }

    public List<N> getChildren() {
        return children;
    }
}

class SpecialNode extends Node<SpecialNode> {
    public SpecialNode(SpecialNode parent) {
        super(parent);
    }

    SpecialNode getThis() {
        return this;
    }

}

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

Node<? extends Node<? extends N, ? extends T>, T> doSomething(
    Node<? super Node<? extends N>, ? extends T> p, 
    Node<? extends Node<? super N>, ? super T> c) { ... }

, которые являются безопасными по типу, или такими методами, как

Node doSomething(Node parent, Node child) { ... }

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

2
ответ дан Community 26 August 2018 в 12:45
поделиться

Похоже, что в настоящее время Some<E extends Some<E>> не имеет приключений по сравнению с Some<E extends Some>

Согласно статье , написанной в сочетании с Java Champions и создателем часто задаваемых вопросов Java Generics :

Статья допускает некоторые возможности / предположения:

  • , поскольку Enum является общим классом, мы должны использовать его только в сочетании с типом
  • Java может стать более строгим, а необработанные типы могут стать незаконными

Я думаю, что Enum было бы достаточно. Однако, как указал мне Филипп Вадлер, поскольку Enum - это общий класс, мы должны использовать его только в сочетании с типом. В будущем Java может стать более строгим, а необработанные типы могут стать незаконными. Поэтому у нас есть выбор записи либо Enum<E extends Enum<E>>, либо Enum<E extends Enum<?>>, из которых первый вариант является более точным. Несмотря на то, что компилятор в настоящее время не показывает мне разницы, я могу увидеть предупреждения в будущем, поэтому я следую этой идиоме в своем классе

0
ответ дан Kinga Odecka 26 August 2018 в 12:45
поделиться

Очень мало ситуаций, когда необходима привязка как class Some<E extends Some<E>>. Большую часть времени люди пишут это, в действительности код не используется в коде, а class Some<E> будет работать так же хорошо.

Однако существуют некоторые ситуации, в которых оценка в class Some<E extends Some<E>> фактически используется. Например:

abstract class Some<E extends Some<E>> {
    abstract E foo();
    Some<E> bar() {
        return foo();
    }
}

Что касается вашего вопроса - как насчет class Some<E extends Some>? Ну, первая наиболее вопиющая проблема заключается в том, что вы используете сырой тип. Необработанные типы никогда не должны использоваться в новом коде. Но вы не уверены.

Необработанный тип с указанным выше классом (class Some<E extends Some>) компилирует с предупреждением (которое вы можете игнорировать на свой страх и риск). Тем не менее, тип raw означает, что с ним можно делать небезопасные вещи.

Требуется некоторое усилие, чтобы продемонстрировать, что это небезопасно. Вот один из них:

abstract class Some<E extends Some> {
    abstract E foo();
    Some<E> bar() {
        return foo();
    }
}

class SomeFoo extends Some<SomeFoo> {
    SomeFoo foo() { return this; }
}

class SomeBar extends Some<SomeFoo> {
    SomeFoo foo() { return new SomeFoo(); }
}

class SomeBaz extends Some<SomeBar> {
    SomeBar foo() { return new SomeBar(); }
}

// then in some method:
Some<SomeBar> a = new SomeBaz();
Some<SomeBar> b = a.bar();
SomeBar c = b.foo();

Код компилируется с предупреждениями, но без ошибок, и выдает ClassCastException во время выполнения.

3
ответ дан newacct 26 August 2018 в 12:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: