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

#java #types #set #type-safety

#java #типы #set #безопасность типов

Вопрос:

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

Ответ №1:

вы можете использовать Object.getClass() для определения типа ваших объектов.

если вам нужна гарантия того, что добавляются только классы типа MyClass, вы могли бы просто расширить HashSet и переопределить add(Object o) метод и добавить только элемент в коллекцию if (o.getClass() == MyClass.class) :

Обновление: добавлен рабочий пример, который вы можете запускать как PoC.

 public class MySet extends HashSet<MySet.MyClass> {
    public static class MyClass {}

    public static class MySubClass extends MyClass {}

    @Override
    public boolean add(MyClass c) {
        if (c.getClass() != MyClass.class) {
            throw new IllegalArgumentException("illegal class to add "   c.getClass().getName());
        }
        return super.add(c);
    }

    public static void main(String[] args) {
        MySet set = new MySet();
        set.add(new MyClass());   // works
        set.add(new MySubClass()); // throws Exception
    }

}
  

выполнение этого примера дает:

Исключение в потоке «main» java.lang.Исключение IllegalArgumentException: недопустимый класс для добавления MySet$MySubClass

Комментарии:

1. Я не думаю, что это сработает, поскольку вы не можете переопределить add() и изменить тип его параметров одновременно. Возможно, вы имели в виду add(object e) вместо этого.

2. этот пример работает вы можете добавить метод main и использовать HashSet<String> и getClass()!=String.class . я проверил это.

3. @Peter: также типом аргумента add() является общий тип E из HashSet<E> -> add(E o) поэтому HashSet<MyClass> имеет add(MyClass o) : download.oracle.com/javase/1,5.0/docs/api/java/util/…

4. Вы правы, я думал о таких методах, как remove(Object) и contains(Object)

Ответ №2:

Хорошо, я думаю, что я совершенно неправильно понял суть.

Я бы, вероятно, использовал композицию, а не наследование, делегируя HashSet<T> внутренне, но в add вы бы хотели проверить:

 if (o.getClass() != clazz)
{
     // throw or reject
}
  

Обратите внимание, что это означает, что вам понадобится фактический класс во время выполнения, поэтому вам нужно будет создать набор с чем-то вроде:

 ExactTypeSet<String> x = new ExactTypeSet<String>(String.class);
  

Ответ №3:

В очень специализированном случае, когда вы знаете тип класса, который хотите добавить, вы могли бы расширить существующую реализацию Set и переопределить add(Object o) метод, разрешив добавлять объекты только там, где o.getClass().getName().equals("yourclassname") .

Комментарии:

1. Проверка фактического класса была бы проще. Возможно иметь два класса в разных загрузчиках классов с одинаковым именем. (Я склонен находить это сбивающим с толку и избегать этого;)

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