Вам нужно установить значение upload_max_filesize
и post_max_size
в вашем php.ini:
; Maximum allowed size for uploaded files.
upload_max_filesize = 40M
; Must be greater than or equal to upload_max_filesize
post_max_size = 40M
. После изменения файлов (ов) php.ini вам необходимо перезапустить HTTP-сервер для использования новой конфигурации.
Если вы не можете изменить свой php.ini, вам не повезло. Вы не можете изменить эти значения во время выполнения; Загрузка файлов большего размера, чем значение, указанное в php.ini, будет сбой, так как время выполнения достигнет вашего вызова ini_set
.
Это означает, что аргумент type для перечисления должен выводиться из перечисления, которое имеет один и тот же аргумент типа. Как это может произойти? Сделав аргумент типа новым типом. Поэтому, если у меня есть перечисление с именем StatusCode, оно будет эквивалентно:
public class StatusCode extends Enum<StatusCode>
Теперь, если вы проверите ограничения, у нас есть Enum<StatusCode>
- так E=StatusCode
. Давайте проверим: расширяет ли E
Enum<StatusCode>
? Да! Мы все в порядке.
Вы вполне можете спросить себя, в чем суть этого :) Ну, это означает, что API для Enum может ссылаться на себя - например, имея возможность сказать, что Enum<E>
реализует Comparable<E>
. Базовый класс способен выполнять сравнения (в случае перечислений), но он может убедиться, что он сравнивает только правильный тип перечислений друг с другом. (EDIT: ну, почти - см. Редактирование внизу.)
Я использовал что-то подобное в моем C # -порте ProtocolBuffers. Существуют «сообщения» (неизменяемые) и «строители» (изменяемые, используемые для создания сообщения), и они являются парами типов. В число задействованных интерфейсов входят:
public interface IBuilder<TMessage, TBuilder>
where TMessage : IMessage<TMessage, TBuilder>
where TBuilder : IBuilder<TMessage, TBuilder>
public interface IMessage<TMessage, TBuilder>
where TMessage : IMessage<TMessage, TBuilder>
where TBuilder : IBuilder<TMessage, TBuilder>
Это означает, что из сообщения вы можете получить соответствующий строитель (например, взять копию сообщения и изменить некоторые биты), а из строителя вы можете получить соответствующий когда вы закончите его строительство. Это хорошая работа, которую пользователи API не нуждаются в этом, хотя это ужасно сложно, и потребовалось несколько итераций, чтобы добраться туда, где они есть.
EDIT: Обратите внимание, что это не останавливается вы создаете нечетные типы, которые используют аргумент типа, который сам по себе хорошо, но который не является одним и тем же типом. Цель состоит в том, чтобы приносить пользу в случае right , а не защищать вас от неправильного случая .
Так что если Enum
не были обработаны " особенно в Java, вы могли бы (как отмечено в комментариях) создать следующие типы:
public class First extends Enum<First> {}
public class Second extends Enum<First> {}
Second
будет реализовывать Comparable<First>
, а не Comparable<Second>
... но First
сам будет хорошо.
Это можно проиллюстрировать простым примером и техникой, которая может быть использована для реализации цепных вызовов методов для подклассов. В примере ниже setName
возвращается значение Node
, поэтому цепочка City
не будет работать:
class Node {
String name;
Node setName(String name) {
this.name = name;
return this;
}
}
class City extends Node {
int square;
City setSquare(int square) {
this.square = square;
return this;
}
}
public static void main(String[] args) {
City city = new City()
.setName("LA")
.setSquare(100); // won't compile, setName() returns Node
}
. Таким образом, мы могли бы ссылаться на подкласс в общем объявлении, City
теперь возвращает правильный тип:
abstract class Node<SELF extends Node<SELF>>{
String name;
SELF setName(String name) {
this.name = name;
return self();
}
protected abstract SELF self();
}
class City extends Node<City> {
int square;
City setSquare(int square) {
this.square = square;
return self();
}
@Override
protected City self() {
return this;
}
public static void main(String[] args) {
City city = new City()
.setName("LA")
.setSquare(100); // ok!
}
}
Если вы посмотрите на исходный код Enum
, он имеет следующее:
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
}
Прежде всего, что означает E extends Enum<E>
? Это означает, что параметр типа является чем-то, что простирается от Enum и не параметризуется с необработанным типом (он сам параметризуется).
Это актуально, если у вас есть перечисление
public enum MyEnum {
THING1,
THING2;
}
, который, если я правильно знаю, переведен на
public final class MyEnum extends Enum<MyEnum> {
public static final MyEnum THING1 = new MyEnum();
public static final MyEnum THING2 = new MyEnum();
}
Таким образом, это означает, что MyEnum получает следующие методы:
public final int compareTo(MyEnum o) {
Enum<?> other = (Enum<?>)o;
Enum<MyEnum> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
И что еще более важно,
@SuppressWarnings("unchecked")
public final Class<MyEnum> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper;
}
Это делает getDeclaringClass()
отличным от соответствующего объекта Class<T>
.
Ярким примером является тот, на который я ответил на этот вопрос , где вы не можете избежать этой конструкции, если хотите указать общую привязку.
Вы не единственный, кто задается вопросом, что это значит; см. Хаотический Java-блог .
«Если класс расширяет этот класс, он должен передать параметр E. Оценки параметра E для класса, который расширяет этот класс с тем же параметром E».
Ниже приведен модифицированный вариант объяснения из книги Java Generics and Collections : у нас есть Enum
объявленный
enum Season { WINTER, SPRING, SUMMER, FALL }
, который будет расширен до class
final class Season extends ...
, где ...
должен быть как-то параметризованным базовым классом для Enums. Давайте выясним, что это должно быть. Ну, одним из требований для Season
является то, что он должен реализовывать Comparable<Season>
. Итак, нам понадобится
Season extends ... implements Comparable<Season>
. Что вы могли бы использовать для ...
, который позволил бы этому работать? Учитывая, что он должен быть параметризацией Enum
, единственным выбором является Enum<Season>
, так что вы можете иметь:
Season extends Enum<Season>
Enum<Season> implements Comparable<Season>
Таким образом, Enum
параметризуется в таких типах, как Season
. Реферат из Season
и вы получите, что параметр Enum
- это любой тип, который удовлетворяет
E extends Enum<E>
Морис Нафталин (соавтор, Java Generics and Collections)
В случае Enum
это бесполезно. Все будет работать одинаково, если оно было объявлено как
class Enum<E>
Этот пост полностью разъяснил мне эту проблему «рекурсивных родовых типов». Я хотел бы добавить еще один случай, когда эта конкретная структура необходима.
Предположим, что у вас есть общие узлы в общем графике:
public abstract class Node<T extends Node<T>>
{
public void addNeighbor(T);
public void addNeighbors(Collection<? extends T> nodes);
public Collection<T> getNeighbor();
}
Тогда вы можете иметь графики специализированных типов:
public class City extends Node<City>
{
public void addNeighbor(City){...}
public void addNeighbors(Collection<? extends City> nodes){...}
public Collection<City> getNeighbor(){...}
}