Как параметризовать статический метод java
Перейти к содержимому

Как параметризовать статический метод java

  • автор:

Generic Methods

Consider writing a method that takes an array of objects and a collection and puts all objects in the array into the collection. Here's a first attempt:

By now, you will have learned to avoid the beginner's mistake of trying to use Collection<Object> as the type of the collection parameter. You may or may not have recognized that using Collection<?> isn't going to work either. Recall that you cannot just shove objects into a collection of unknown type.

The way to do deal with these problems is to use generic methods. Just like type declarations, method declarations can be generic—that is, parameterized by one or more type parameters.

We can call this method with any kind of collection whose element type is a supertype of the element type of the array.

Notice that we don't have to pass an actual type argument to a generic method. The compiler infers the type argument for us, based on the types of the actual arguments. It will generally infer the most specific type argument that will make the call type-correct.

One question that arises is: when should I use generic methods, and when should I use wildcard types? To understand the answer, let's examine a few methods from the Collection libraries.

We could have used generic methods here instead:

However, in both containsAll and addAll , the type parameter T is used only once. The return type doesn't depend on the type parameter, nor does any other argument to the method (in this case, there simply is only one argument). This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites. If that is the case, one should use wildcards. Wildcards are designed to support flexible subtyping, which is what we're trying to express here.

Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used.

It is possible to use both generic methods and wildcards in tandem. Here is the method Collections.copy() :

Note the dependency between the types of the two parameters. Any object copied from the source list, src , must be assignable to the element type T of the destination list, dst . So the element type of src can be any subtype of T —we don't care which. The signature of copy expresses the dependency using a type parameter, but uses a wildcard for the element type of the second parameter.

We could have written the signature for this method another way, without using wildcards at all:

This is fine, but while the first type parameter is used both in the type of dst and in the bound of the second type parameter, S , S itself is only used once, in the type of src —nothing else depends on it. This is a sign that we can replace S with a wildcard. Using wildcards is clearer and more concise than declaring explicit type parameters, and should therefore be preferred whenever possible.

Wildcards also have the advantage that they can be used outside of method signatures, as the types of fields, local variables and arrays. Here is an example.

Returning to our shape drawing problem, suppose we want to keep a history of drawing requests. We can maintain the history in a static variable inside class Shape , and have drawAll() store its incoming argument into the history field.

Finally, again let's take note of the naming convention used for the type parameters. We use T for type, whenever there isn't anything more specific about the type to distinguish it. This is often the case in generic methods. If there are multiple type parameters, we might use letters that neighbor T in the alphabet, such as S . If a generic method appears inside a generic class, it's a good idea to avoid using the same names for the type parameters of the method and class, to avoid confusion. The same applies to nested generic classes.

Статические методы и поля. Модификатор native. Модификатор synchronized. Логические блоки.

Поля данных, объявленные в классе как static, являются общими для всех объектов класса и называются переменными класса. Если один объект изменит значение такого поля, то это изменение увидят все объекты.

Вызов статического метода также следует осуществлять с помощью указания: ClassName.methodName().

Приложение на языке Java может вызывать методы, написанные на языке С++. Такие методы объявляются с ключевым словом native, которое сообщает компи- лятору, что метод реализован в другом месте. Например: public native int loadCripto(int num); Методы, помеченные native, можно переопределять обычными методами в подклассах.

При использовании нескольких потоков управления в одном приложении необходимо синхронизировать методы, обращающиеся к общим данным. Когда интерпретатор обнаруживает synchronized, он включает код, блокирующий доступ к данным при запуске потока и снимающий блок при его завершении. Вызов методов уведомления о возвращении блокировки объекта notifyAll(), notify() и метода остановки потока wait() класса Object (суперкласса для всех классов языка Java) предполагает использование модифи- катора synchronized, так как эти методы предназначены для работы с пото- ками.

При описании класса могут быть использованы логические блоки. Логиче- ским блоком называется код, заключенный в фигурные скобки и не принадлежа- щий ни одному методу текущего класса, например: < /* код */ >static < /* код */ >При создании объекта класса они вызываются последовательно, в порядке разме- щения, вместе с инициализацией полей как простая последовательность операто- ров, и только после выполнения последнего блока будет вызван конструктор класса. Операции с полями класса внутри логического блока до явного объявле- ния этого поля возможны только при использовании ссылки this, представляю- щую собой ссылку на текущий объект.

Параметризованные классы. Параметризованные методы. Методы с переменным числом параметров.

К наиболее важным новшествам версии языка J2SE 5 можно отнести появле- ние параметризованных (generic) классов и методов, позволяющих использовать более гибкую и в то же время достаточно строгую типизацию, что особенно важ- но при работе с коллекциями. Параметризация позволяет создавать классы, интерфейсы и методы, в которых тип обрабатывае- мых данных задается как параметр.

Объявление generic-типа в виде <T>, несмотря на возможность использо- вать любой тип в качестве параметра, ограничивает область применения разра- батываемого класса. Переменные такого типа могут вызывать только методы класса Object. Доступ к другим методам ограничивает компилятор, предупре- ждая возможные варианты возникновения ошибок. Чтобы расширить возможности параметризованных членов класса, можно ввести ограничения на используемые типы при помощи следующего объявления класса: public class OptionalExt <T extends Tип> < private T value; >Такая запись говорит о том, что в качестве типа Т разрешено применять толь- ко классы, являющиеся наследниками (суперклассами) класса Tип, и соответ- ственно появляется возможность вызова методов ограничивающих (bound) типов.

Параметризованный (generic) метод определяет базовый набор операций, ко- торые будут применяться к разным типам данных, получаемых методом в каче- стве параметра, и может быть записан, например, в виде: <T extends Тип> returnType methodName(T arg) <> <T> returnType methodName(T arg) <> Описание типа должно находиться перед возвращаемым типом. Запись пер- вого вида означает, что в метод можно передавать объекты, типы которых явля- ются подклассами класса, указанного после extends. Второй способ объявления метода никаких ограничений на передаваемый тип не ставит. Generic-методы могут находиться как в параметризованных классах, так и в обычных. Параметр метода может не иметь никакого отношения к параметру сво- его класса.

Возможность передачи в метод нефиксированного числа параметров позволя- ет отказаться от предварительного создания массива объектов для его последую- щей передачи в метод.

public class DemoVarargs <

public static int getArgCount(Integer. args)

if (args.length == 0) System.out.print(«No arg arg:» + i + » «);

public static void main(String args[])

System.out.println(«N N 7″>

Типобезопасные перечисления (typesafe enums) в Java представляют собой классы и являются подклассами абстрактного класса java.lang.Enum. При этом объекты перечисления инициализируются прямым объявлением без помощи оператора new. При инициализации хотя бы одного перечисления происходит инициализация всех без исключения оставшихся элементов данного перечисления.

Перечисление как подкласс класса Enum может содержать поля, конструкто- ры и методы, реализовывать интерфейсы. Каждый тип enum может использо- вать методы: static enumType[] values() – возвращает массив, содержащий все элементы перечисления в порядке их объявления; static T valueOf(Class<T> enumType, String arg) – возвра- щает элемент перечисления, соответствующий передаваемому типу и значению передаваемой строки; static enumType valueOf(String arg) – возвращает элемент пере- числения, соответствующий значению передаваемой строки; int ordinal() – возвращает позицию элемента перечисления.

Параметризованный метод Java

Мне же нужно написать метод, который принимал бы на вход потомка класса А (т.е. объект класса либо В либо С) и процессе какое-то количество раз создавал новый экземпляр переданного класса. Как это сделать?

UPD Реализовал вот так:

Однако в таком случае вызов геттера для поля name вернет null. В чем проблема?

Nofate's user avatar

Пример 1. (Приведение типов)

Если в метод передать только объект, то происходит «стирание типов». И виртуальная машина не знает, какой именно тип она получила на входе, и расценивает как «Любой унаследованный от класса А«. Но Java — язык со строгой типизацией и метод не может вернуть «Объект любого класса» в связи с чем в коде появляются строки:

То есть строгое приведение типа и игнорирование предупреждения компилятора. Использование:

Core Java. Лекция 6

rtvscompiletime

Написание собственных параметризованных классов и методов — задачка более сложная.

Bounded types

Intersection types

intersections

Реализация дженериков

Появились в Java 5

Стояла задача обратной совместимости

Generics — возможность языка, а не платформы

Type Erasure, будь он неладен!

Сырые типы

Generic Type (source)

Raw Type (compiled)

Ограниченные типы вместо Object

Generic Type (source)

Raw Type (compiled)

Вызовы методов

Source code

Compiled

Bridge methods для сохранения полиморфизма

Source code

Compiled

Итог: как это работает

Параметризованных классов в JVM нет, только обычные классы и методы.

Типовые параметры заменяются на Object или на свою границу.

Для сохранения полиморфизма добавляются связывающие методы (bridge methods).

Сведение типов добавляется по мере необходимости.

Никогда не употребляйте сырые типы

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

Java5 вышла в 2004 году.

Понимание дженериков в Джаве — это не про то, что с ними делать можно, а про то, что с ними делать нельзя.

Стирание типов → невозможность определить параметр типа в Runtime

rawtype

Стирание типов до Object → невозможность использовать примитивные типы в качестве параметров

Примитивы и дженерики

День сегодняшний: нужна производительность? — пишем специальные реализации.

В стандартной библиотеке:

В специализированных библиотеках вроде fastutil:

HashMap<Integer, V> → Int2ObjectMap<V> (ВНИМАНИЕ: реальная потребность в таких библиотеках возникает редко!!)

День завтрашний: Project Valhalla, specialized generics. Решит проблему раз и навсегда.

Нельзя инстанцировать типы-параметры

Решается с помощью метакласса и рефлексии (о которой речь впереди)

Тем более нельзя инстанцировать массив типа-параметра

Решается передачей параметра, например, в ArrayList:

Массивы и дженерики — лютые враги

Забьём значения кувалдой и устроим heap pollution

Varargs — всё равно массив…​

Тот же heap pollution, что и в прошлом примере:

Компилятор что-то предчувствует…​

varargswarning

Чтобы успокоить компилятор, надо поставить аннотацию @SafeVarargs :

…​и компилятор успокоится.

Зачем?!

Всё потому, что иметь varargs с параметризованными типами удобно.

Collections.addAll(Collection<? super T> c, T…​ elements)

EnumSet.of(E first, E…​ rest)

Если вести себя хорошо, можно ставить @SafeVarargs, и всё будет хорошо:

Не записывать ничего в элементы массива,

Не раздавать ссылку на массив параметров наружу.

Нельзя параметризовать

ловля исключений — это проверка их типов,

дальше сырых типов мы в рантайме проверить не можем.

Инстанцируется по месту, не может быть несколько классов, параметризованных по-разному.

Параметры типов нельзя использовать в статическом контексте

Нельзя реализовывать разные параметризации одного и того же интерфейса

Source code

Compiled

Ковариантность массивов vs инвариантность дженериков

manemp

covariance

invariance

Реальная картина

realpicture

Как быть, если хочется такого?

manempperson

Так не получится…​

Java Covariant Wildcard Types

wildext

Что можно сделать с объектом, типизированным ? extends ?

В общем, addAllTo реализовать не получится…​

В обратную сторону (контравариантные типы)

wildsup

Что можно сделать с объектом, типизированным ? super ?

Unbounded wildcard

List<?>  — это то же, что List<? extends Object> . (Вопрос: почему не <? super Object> ?)

Брать элементы можем, но тип только Object .

Класть можем только null.

Мнемоническое правило

PECS

Producer Extends, Consumer Super

Правила использования wildcard-типов

Используются в аргументах методов и локальных переменных.

Невидимы пользователю API, не должны возвращаться.

Их цель — принимать те аргументы, которые надо принимать, и отвергать те аргументы, которые надо отвергать.

Должны быть используемы в API, иначе API будет слишком «жёстким» и непригодным для использования.

Wildcard Capture

Метод с type capture

Recursive Generics

Что почитать-посмотреть

J. Bloch, Effective Java, 3rd ed. Chapter 5 — Generics. Addison-Wesley, 2018

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *