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

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

Различные фильтры могут помочь с различными задачами. В то время как mysql_real_escape_string хорошо для очистки данных для предотвращения Внедрения SQL, это бесполезно для вывода данных, которые могут содержать HTML. Вот несколько фильтров, которые я использовал бы для повседневных задач:

  • FILTER_SANITIZE_SPECIAL_CHARS - полезный для отображения (не удаляющий) код HTML, предотвращая нападения на XSS и преобразовывая символы в объекты HTML.
  • FILTER_SANITIZE_STRING с STRIP_LOW/HIGH флаги - на самом деле удаляют HTML (см. strip_tags).
  • FILTER_SANITIZE_URL - делает URL safe*.
  • FILTER_SANITIZE_EMAIL - делает адреса электронной почты безопасными, хотя я предпочел бы использовать, это - кузен проверки прежде, чем сохранить адрес.

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

5
задан stema 15 March 2012 в 09:19
поделиться

9 ответов

Я хочу определить список, который может принимать объекты только двух типов (определенных выше). Любая попытка добавления, отличная от этих двух типов, должна вызвать у меня сообщение «ОШИБКА ВРЕМЕНИ КОМПИЛЯЦИИ»

Это действительно то, о чем вы просите (с ошибкой времени выполнения):

public class MyList extends ArrayList<Object> {

    public MyList() {
        super();
    }

    public MyList(int initialSize) {
        super(initialSize);
    }

    @Override
    public void add(Object obj) {
        if ((obj instanceof String) || (obj instanceof SomeType)) {
            add(obj);
        } else {
            throw new IllegalArgumentException("not a String or SomeType");
        }
    }

    public void add(String s) {
        super.add(s);
    }

    public void add(SomeType s) {
        super.add(s);
    }
}

Невозможно реализовать это в Java, которое приведет к ошибке времени компиляции. Вы добавляете элемент неправильного типа (в вашем понимании) в Список . Однако, если бы это был не список List , вы могли бы определить класс для перегруженных методов add и т. Д. Создание новых методов "сумматора" здесь не поможет, потому что существующий метод add (T) по-прежнему будет существовать в интерфейсе. Независимо от того, что вы делаете (в Java), вызов этого не будет ошибкой времени компиляции.

2
ответ дан 18 December 2019 в 07:30
поделиться

Я не собираюсь утверждать, что это идеальное решение, но я собираюсь порекомендовать вам использовать класс «держатель», который некоторые также называют «помеченным классом». писатели (включая Джошуа Блоха, который говорит, что классы с тегами «многословны, подвержены ошибкам и неэффективны» ).

Однако, учитывая вашу ситуацию, я не вижу лучшего пути. Приведенное ниже решение обеспечивает:

  • Безопасность типов во время компиляции при вставке типов без возможного общего предка
  • Возможность заменить текущий список какой-либо другой коллекцией без необходимости реализации и тестирования какого-либо дополнительного кода
  • Возможность для использования класса-держателя в других контекстах

Вы должны определить свой класс-держатель следующим образом:

class Holder {
  private String foo;
  private UserClass bar;
  boolean isString;
  boolean initialized=false;
  Holder (String str) { foo = str; isString=true; }
  Holder (UserClass bar) { this.bar = bar; isString=false; }
  String getStringVal () { 
      if (! initialized) throw new IllegalStateException ("not initialized yet");
      if (! isString) throw new IllegalStateException ("contents not string");
      return foo;
  }
  // with a similar method for getUserClassVal()
...
}

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

Тогда, конечно, у вас будет составной список:

   List samsList = new ArrayList()

Вставки просты и, как вы просили, безопасны для типов во время компиляции:

   samsList.add (new Holder(stringVal));
   samsList.add (new Holder(userClassVal));

Получение значений из списка немного сложнее: вы должны проверить тег (Holder.isString ()), прежде чем решить, какой геттер использовать. В качестве примера итерация foreach по списку будет выглядеть следующим образом:

   for (Holder holder: samsList) {
      if (holder.isString())
         doYourStringProcessing (holder.getStringVal());
      else
         doYourUserClassProcessing (holder.getUserClassVal());
   }

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

Однако , я хотел бы отметить, что мне кажется, что это, вероятно, повод подумать о рефакторинге / редизайне. Одно из правил, которым я следую, заключается в том, что всякий раз, когда я оправдываю исключение из разумной практики, он заслуживает гораздо большего внимания, чем просто «как я могу это сделать?».

Вот почему: если предположить, что я прав в том, что в данном случае исключение оправдано, то на самом деле есть только две возможности. Один из них заключается в том, что здравая практика является неполной (так что «Предпочитать X вместо Y» следует переписать как «Предпочитать X вместо Y, кроме случая Z»).

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

8
ответ дан 18 December 2019 в 07:30
поделиться

Поскольку String является непосредственным подклассом Object и является окончательным, вы не найдете общего супертипа между String и определяемым пользователем классом. кроме Объект . Итак, List - это то, что вы должны использовать.

С точки зрения дизайна, смешивание несвязанных классов в коллекции - плохая идея. Подумайте о том, чего вы пытаетесь достичь, и вы, вероятно, придумаете лучший подход.

7
ответ дан 18 December 2019 в 07:30
поделиться

Если вы имеете в виду «типобезопасный», например, при проверке безопасности типов во время компиляции, то попытка использовать дженерики для решения этой проблемы будет трудной.

Основная причина в том, что поскольку класс String является final , поэтому невозможно создать подкласс из String .

Если бы создание подкласса String было возможно, можно было бы включить как String , так и определяемый пользователем подкласс String в список, объявленный как ] Список :

// Not possible.
List<? extends String> list = new ArrayList<? extends String>;
list.add("A string");
list.add(new UserDefinedSubclassOfString());   // There can be no such class.

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

class MyList {

    List<String> strings;
    List<UserDefined> objects;

    public void add(String s) {
        strings.add(s);
    }

    public void add(UserDefined o) {
        objects.add(o);
    }

    // And, so on.
}

Проблема с этим подходом, однако, в том, что он выиграл ' Можно использовать интерфейс List , поскольку он ожидает, что параметр будет иметь тип E . Следовательно, использование Object или ? в качестве параметра (например, List или List соответственно) приведет к нарушить эту цель, поскольку не может быть проверки типов во время компиляции, поскольку все классы в Java имеют Object в качестве своего предка.

Одна вещь, о которой следует подумать, - как обрабатывать получение объектов из это гипотетический MyList . Если бы существовал единственный метод get , возвращаемый тип должен был бы быть общим предком классов String и UserDefined . Это будет Объект .

Единственный способ обойти это - предоставить два геттера, по одному для каждого типа. Например, getString и getUserDefined . На этом этапе должно быть очевидно, что невозможно использовать интерфейс List , что потребовало бы возврата типа E в get ] метод.

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

Чтобы получить представление о том, что такое дженерики и что с ними возможно и невозможно, неплохо было бы начать Урок: Дженерики из Учебники по Java .

1
ответ дан 18 December 2019 в 07:30
поделиться

Как говорит kdgregory, вы, вероятно, захотите переосмыслить свой подход. Может быть, два списка, List и List . Может быть List , содержащий строку или объект пользователя. Возможно, класс, содержащий этот список, хочет стать class Blah вместо class Blah , где T тогда может быть String , UserClass или любой другой.

0
ответ дан 18 December 2019 в 07:30
поделиться

Или, может быть, один список, который принимает пользовательский тип MyType, который инкапсулирует вашу строку и все, что вам нужно, в единую абстракцию.

Если вы думаете в терминах примитивов и структур данных все время нужно поднять свой прицел. Объектная ориентация - это инкапсуляция, абстракция и сокрытие информации. Мне кажется, что вы делаете недостаточно.

0
ответ дан 18 December 2019 в 07:30
поделиться

Мой вопрос в том, для чего предполагается использовать "определяемый пользователем класс"? Без этих знаний трудно дать хороший совет. Не пользователи создают классы, а программисты. Вы разрабатываете какую-то общую структуру?

Какова деловая цель ваших списков? Предполагается ли, что String будет использоваться как своего рода тип по умолчанию, если не указан пользовательский класс? В этом случае вы можете просто настроить BaseUserDefinedClass и использовать такие списки, как:

List<? extends BaseUserDefinedClass> = new ArrayList<DerivedFromBaseUserDefinedClass>();
0
ответ дан 18 December 2019 в 07:30
поделиться

Это зависит от того, для чего вы используете список ...

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

List<String> stringList = new ArrayList<String>();
// Add String
stringList.add("myString");
// add YourObject
YourObject obj = new YourObject(...);
stringList.add(obj.toString());
// ...
for(String s : stringList) {
    System.out.println(s);
}

Если вы собираетесь использовать его для получения ссылок на YourObject:

List<YourObject> objList = new ArrayList<YourObject>();
// Add String 
objList.add(new YourObjectAdapter("myString"));
// add YourObject
YourObject obj = new YourObject(...);
objList.add(obj)
// ...
for (YourObject y : objList) {
    System.out.println(y.toString());
    // Assuming YourObject defines the "doSomething() method"
    y.doSomething();
}
// ...
class YourObjectAdapter extends YourObject {
    private String wrappedString;
    public YourObjectAdapter(String s) {
        this.wrappedString = s;
    }
    @Override
    public void toString() {
        return wrappedString();
    }
    @Override
    public void doSomething() {
      // provide some default implementation...
    }
}
0
ответ дан 18 December 2019 в 07:30
поделиться

См. Книгу «Эффективная Java», пункт 29: Учитывайте типизированные гетерогенные контейнеры. Возможно, вы сможете адаптировать идеи в этом элементе к своему конкретному варианту использования.

Я предполагаю, что вы хотите использовать свой List как List , но вы хотите только разрешить вставку String s и некоторых других конкретных типов? Это то, чего вы пытаетесь достичь?

Если да, вы можете сделать что-то вроде этого:

import java.util.*;

public class HetContainer {
  Set<Class<?>> allowableClasses;

  List<Object> items;

  public HetContainer(List<Class<?>> allowableClasses) {
    this.allowableClasses = new HashSet<Class<?>>(allowableClasses);
    items = new ArrayList<Object>();
  }

  public void add(Object o) {
    if (allowableClasses.contains(o.getClass())) {
      items.add(o);
    } else {
      throw new IllegalArgumentException("Object of type " + o.getClass() + " is not allowed.");
    }
  }

  public Object get(int i) {
    return items.get(i);
  }

  public static void main(String[] args) {
    List<Class<?>> classes = new ArrayList<Class<?>>();
    classes.add(String.class);
    classes.add(Integer.class);
    HetContainer h = new HetContainer(classes);
    h.add("hello");
    h.add(new Integer(5));
    try {
      h.add(new Double(5.0));
    } catch (IllegalArgumentException e) {
      System.out.println(e);
    }
  }
}

Это просто упрощено, чтобы показать вам, что вы можете делать. Также есть одно предостережение: вы не можете помещать универсальные типы в контейнер ... почему вы спрашиваете? Потому что невозможно сказать List .class или List .class . Причина в "стирании" ... во время выполнения, оба - просто List s. Таким образом, вы можете поместить необработанный список в HetContainer , но не общий список . Опять же, прочтите «Эффективная Java», чтобы понять все ограничения Java и адаптировать ее под свои нужды.

0
ответ дан 18 December 2019 в 07:30
поделиться
Другие вопросы по тегам:

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