Вы корректируетесь 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
- делает адреса электронной почты безопасными, хотя я предпочел бы использовать, это - кузен проверки прежде, чем сохранить адрес. * я использую безопасный свободно, я имею мнение, что Вы никогда не можете быть слишком уверены.
Я хочу определить список, который может принимать объекты только двух типов (определенных выше). Любая попытка добавления, отличная от этих двух типов, должна вызвать у меня сообщение «ОШИБКА ВРЕМЕНИ КОМПИЛЯЦИИ»
Это действительно то, о чем вы просите (с ошибкой времени выполнения):
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), вызов этого не будет ошибкой времени компиляции.
Я не собираюсь утверждать, что это идеальное решение, но я собираюсь порекомендовать вам использовать класс «держатель», который некоторые также называют «помеченным классом». писатели (включая Джошуа Блоха, который говорит, что классы с тегами «многословны, подвержены ошибкам и неэффективны» ).
Однако, учитывая вашу ситуацию, я не вижу лучшего пути. Приведенное ниже решение обеспечивает:
Вы должны определить свой класс-держатель следующим образом:
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»).
Но гораздо более вероятно, что лежащее в основе предложение является несовершенным дизайном, и мы должны хорошенько подумать о том, чтобы провести некоторый редизайн / рефакторинг.
Поскольку String
является непосредственным подклассом Object
и является окончательным, вы не найдете общего супертипа между String и определяемым пользователем классом. кроме Объект
. Итак, List
- это то, что вы должны использовать.
С точки зрения дизайна, смешивание несвязанных классов в коллекции - плохая идея. Подумайте о том, чего вы пытаетесь достичь, и вы, вероятно, придумаете лучший подход.
Если вы имеете в виду «типобезопасный», например, при проверке безопасности типов во время компиляции, то попытка использовать дженерики для решения этой проблемы будет трудной.
Основная причина в том, что поскольку класс String
является final
, поэтому невозможно создать подкласс из String
.
Если бы создание подкласса String
было возможно, можно было бы включить как String
, так и определяемый пользователем подкласс String
в список, объявленный как ] Список extends 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 .
Как говорит kdgregory, вы, вероятно, захотите переосмыслить свой подход. Может быть, два списка, List
и List
. Может быть List
, содержащий строку или объект пользователя. Возможно, класс, содержащий этот список, хочет стать class Blah
вместо class Blah
, где T
тогда может быть String
, UserClass
или любой другой.
Или, может быть, один список, который принимает пользовательский тип MyType, который инкапсулирует вашу строку и все, что вам нужно, в единую абстракцию.
Если вы думаете в терминах примитивов и структур данных все время нужно поднять свой прицел. Объектная ориентация - это инкапсуляция, абстракция и сокрытие информации. Мне кажется, что вы делаете недостаточно.
Мой вопрос в том, для чего предполагается использовать "определяемый пользователем класс"? Без этих знаний трудно дать хороший совет. Не пользователи создают классы, а программисты. Вы разрабатываете какую-то общую структуру?
Какова деловая цель ваших списков? Предполагается ли, что String будет использоваться как своего рода тип по умолчанию, если не указан пользовательский класс? В этом случае вы можете просто настроить BaseUserDefinedClass и использовать такие списки, как:
List<? extends BaseUserDefinedClass> = new ArrayList<DerivedFromBaseUserDefinedClass>();
Это зависит от того, для чего вы используете список ...
Если вы собираетесь чтобы просто использовать его для получения строк, вы можете просто использовать:
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...
}
}
См. Книгу «Эффективная 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
или List
. Причина в "стирании" ... во время выполнения, оба - просто List
s. Таким образом, вы можете поместить необработанный список
в HetContainer
, но не общий список
. Опять же, прочтите «Эффективная Java», чтобы понять все ограничения Java и адаптировать ее под свои нужды.