- Как параметризовать статический метод в дженериках Java? Как они типизируются?
- Как параметризировать статический метод java
- Дженерики Java
- 1. Параметризованные классы
- 2. Ограниченные типы
- 3. Применение метасимвольных аргументов
- 4. Параметризованные методы и конструкторы
- 5. Параметризованные интерфейсы
- 6. Иерархии параметризованных классов
- 7. Использование оператора instanceof c параметризованными классами
- 8. Ограничения присущие обобщениям
- Как параметризовать статический метод в дженериках Java? Как они типизируются?
Как параметризовать статический метод в дженериках Java? Как они типизируются?
Дженерики в Java существуют только во время компиляции. Это значит, что во время выполнения нельзя достать из них информацию. Поэтому, чтобы параметризовать метод, нужно передать ему объект, в котором будет содержаться информация о типе. Это может быть либо просто объект этого типа или объект-класс.
Если передаётся класс и в методе происходит создание объекта или вызов методов у него, придётся обрабатывать ситуации, когда конструктор или метод отсутствует или к ним нет доступа.
public static T myMethod(Class aClass) throws ReflectiveOperationException < return aClass.newInstance(); >public static void main(String[] args) < try < String a = myMethod(String.class); System.out.println(a.length()); >catch (ReflectiveOperationException e) < e.printStackTrace(); >>
Для более удобного доступа к свойствам и методам можно использовать commons-beanutils, для вызова конструктора — ConstructorUtils из той же библиотеки или из commons-lang3, а с помощью Optional можно спрятать все исключения под капотом, не выбрасывая null.
import org.apache.commons.lang3.reflect.ConstructorUtils; import java.lang.reflect.Constructor; import java.util.Objects; import java.util.Optional; public class Foo < public static Optional bar(Class aClass) < Constructorctor = ConstructorUtils.getAccessibleConstructor(aClass); if (Objects.nonNull(ctor)) < try < return Optional.of(ctor.newInstance()); >catch (ReflectiveOperationException e) < return Optional.empty(); >> else < return Optional.empty(); >> public static void main(String[] args) < Optionala = bar(String.class); if (a.isPresent()) < System.out.println(a.get().length()); >> >
Как параметризировать статический метод java
Вы можете параметризовать статический метод передавая ему в качестве аргумента обобщенный тип. Например, рассмотрим следующий пример статического метода, который суммирует два числа:
public class MathUtils public static int sum(int a, int b) return a + b; > >
Вы можете параметризовать этот метод, передав ему в качестве аргументов два объекта типа T , где T — это обобщенный тип, как показано в следующем примере:
public class MathUtils public static T extends Number> double sum(T a, T b) return a.doubleValue() + b.doubleValue(); > >
В этом примере мы использовали обобщенный тип для параметризации метода sum() . Здесь мы ограничиваем тип T , чтобы он был типом Number или его подклассом, и таким образом мы можем использовать метод doubleValue() для преобразования значения объектов типа T в тип double
Таким образом, мы можем вызвать параметризованный статический метод sum() следующим образом:
int result = MathUtils.Double>sum(3.5, 2.5); // 6.0
Здесь мы явно указываем, что тип T является типом Double
Дженерики Java
Дженерики (или обобщения) — это параметризованные типы.
Параметризованные типы позволяют объявлять классы, интерфейсы и методы, где тип данных, которыми они оперируют, указан в виде параметра. Используя дженерики, можно создать единственный класс, например, который будет автоматически работать с разными типами данных.
Классы, интерфейсы или методы, имеющие дело с параметризованными типами, называются параметризованными или обобщениями, параметризованными (обобщенными) классами или параметризованными (обобщёнными) методами.
Обобщения добавили в язык безопасность типов.
1. Параметризованные классы
Следующий пример демонстрирует использование параметризованного класса, который описывает матрицу:
public class Matrix < private T[] array; public Matrix(T[] array) < this.array = array.clone(); >public static void main(String[] args) < MatrixdoubleMatrix = new Matrix<>(new Double[2]); Matrix integerMatrix = new Matrix<>(new Integer[4]); Matrix byteMatrix = new Matrix<>(new Byte[7]); > >
В объявлении Matrix integerMatrix Integer является аргументом типа.
Java не создает разные версии класса Matrix или любого другого параметризованного класса. Имеется только одна версия класса Matrix , которая существует в прикладной программе.
Дженерики работают только с объектами! Следующий код является неправильным:
Gen strOb = new Gen (53); // Ошибка, нельзя использовать примитивные типы
Т обозначает имя параметра типа. Это имя используется в качестве заполнителя вместо которого в дальнейшем подставляется имя конкретного типа, передаваемого классу Matrix при создании объекта. Это означает, что обозначение Т применяется в классе Matrix всякий раз, когда требуется параметр типа. Всякий раз, когда объявляется параметр типа, он указывается в угловых скобках.
Обобщенные типы отличаются в зависимости от типов-аргументов. Следующий код не допустим:
doubleMatrix = integerMatrix; // Не верно!
Несмотря на то, что doubleMatrix и integerMatrix имеют тип Matrix , они являются ссылками на разные типы, потому что типы их параметров отличаются.
Обобщенный класс может быть объявлен с любым количеством параметров типа. Например:
public class TwoGen < private T obT; private V obV; public TwoGen(T obT, V obV) < this.obT = obT; this.obV = obV; >public void showTypes() < System.out.println("Тип T: " + obT.getClass().getName()); System.out.println("Тип V: " + obV.getClass().getName()); >public T getObT() < return obT; >public V getObV() < return obV; >> public class SimpleGen < public static void main(String[] args) < TwoGentwoGen = new TwoGen<>(88, "Generics"); twoGen.showTypes(); System.out.println("Значение T: " + twoGen.getObT()); System.out.println("Значение V: " + twoGen.getObV()); > >
2. Ограниченные типы
Указывая параметр типа, можно наложить ограничение сверху в виде верхней границы, где объявляется супер класс, от которого должны быть унаследованы все аргументы типов. С этой целью вместе с параметром указывается ключевое слово extends :
Параметр типа Т может быть заменен только указанным супер классом или его подклассами.
Рассмотрим пример использования ограниченного типа:
public class Average < private T[] array; public Average(T[] array) < this.array = array; >public double average() < double sum = 0.0; for (T value : array) < sum += value.doubleValue(); >return sum / array.length; > > public class AverageDemo < public static void main(String[] args) < Integer[] intArray = ; Average integerAverage = new Average<>(intArray); System.out.println("Среднее значения для Integer " + integerAverage.average()); Double[] doubleArray = ; Average doubleAverage = new Average<>(doubleArray); System.out.println("Среднее значения для Double " + doubleAverage.average()); // Не откомпилируется, // потому что String не является наследником Number /* String[] strArray = ; Average strAverage = new Average<>(strArray); System.out.println("Среднее значения для String " + strAverage.average());*/ > >
В виде ограничения можно накладывать не только тип класса, но и тип интерфейса:
Ограничение может включать в себя как тип класса, так и типы одного или нескольких интерфейсов:
Тип класса должен быть задан первым. Накладывая на обобщенный тип ограничение, состоящее из класса и одного или нескольких интерфейсов, для их объединения следует воспользоваться логической операцией &: Таким образом, любой тип, передаваемый параметру Т , должен быть подклассом, производным от класса MyClass и реализующим интерфейсы MyInterface1 и MyInterface2 .
3. Применение метасимвольных аргументов
Представьте, что мы хотим добавить метод для сравнения средних значений массивов в класс Average из примера 3. Причем типы массивов могут быть разные:
Integer intArray[] = ; Double doubleArray[] = ; Average iob = new Average<>(intArray); Average dob = new Average<>(doubleArray); if (iob.sameAvg(dob)) < System.out.println("are the same.");>else
Так как Average параметризованный тип, какой тип параметра вы укажете для Average , когда создадите параметр метода типа Average ? Напрашивается следующий вариант:
boolean sameAvg(Average ob)
Но это не сработает, так как в этом случае метод sameAvg будет принимать аргументы только того же типа, что и существующий объект:
Чтобы создать обобщенную версию метода sameAvg() , следует воспользоваться другим средством обобщений Jаvа – метасимвольным аргументом. Метасимвольный аргумент обозначается знаком ? и представляет неизвестный тип.
boolean sameAvg(Average ob)
Мета символ не оказывает никакого влияния на тип создаваемых объектов класса Average . Это определяется оператором extends в объявлении класса Average. Мета символ просто совпадает с любым достоверным объектом класса Average .
Метасимвольные аргументы могут быть ограничены почти таким же образом, как и параметры типов. Ограничивать метасимвольный аргумент особенно важно при создании обобщенного типа, оперирующего иерархией классов. Например:
public class Average2 < private T[] array; public Average2(T[] array) < this.array = array.clone(); >public double average() < double sum = 0.0; for (T value : array) < sum += value.doubleValue(); >return sum / array.length; > boolean sameAvg(Average2 ) < System.out.println("object is instance of GenericSuper"); >if (object instanceof GenericSub[] gens = new GenericSub>[10]; gens[0] = new GenericSub<>(34); > >
3. Нельзя создавать обобщенные статические переменные и методы. Но объявить статические обобщенные методы со своими параметрами типа все же можно:
public class GenericWrongStatic < // Неверно, нельзя создать статические переменные типа Т. //public static Т оb; // Неверно, ни один статический метод не может использовать Т. /* public static T getOb() < return оb; >*/ //Но объявить статические обобщенные методы со своими параметрами типа можно public static void getOb(V v) < System.out.println(v); >>
Как параметризовать статический метод в дженериках Java? Как они типизируются?
Дженерики в Java существуют только во время компиляции. Это значит, что во время выполнения нельзя достать из них информацию. Поэтому, чтобы параметризовать метод, нужно передать ему объект, в котором будет содержаться информация о типе. Это может быть либо просто объект этого типа или объект-класс.
Если передаётся класс и в методе происходит создание объекта или вызов методов у него, придётся обрабатывать ситуации, когда конструктор или метод отсутствует или к ним нет доступа.
public static T myMethod(Class aClass) throws ReflectiveOperationException < return aClass.newInstance(); >public static void main(String[] args) < try < String a = myMethod(String.class); System.out.println(a.length()); >catch (ReflectiveOperationException e) < e.printStackTrace(); >>
Для более удобного доступа к свойствам и методам можно использовать commons-beanutils, для вызова конструктора — ConstructorUtils из той же библиотеки или из commons-lang3, а с помощью Optional можно спрятать все исключения под капотом, не выбрасывая null.
import org.apache.commons.lang3.reflect.ConstructorUtils; import java.lang.reflect.Constructor; import java.util.Objects; import java.util.Optional; public class Foo < public static Optional bar(Class aClass) < Constructorctor = ConstructorUtils.getAccessibleConstructor(aClass); if (Objects.nonNull(ctor)) < try < return Optional.of(ctor.newInstance()); >catch (ReflectiveOperationException e) < return Optional.empty(); >> else < return Optional.empty(); >> public static void main(String[] args) < Optionala = bar(String.class); if (a.isPresent()) < System.out.println(a.get().length()); >> >