1. Обзор
В этом уроке мы подробнее рассмотрим ковариантный тип возвращаемого значения в Java. Прежде чем рассматривать ковариацию с точки зрения возвращаемого типа, давайте посмотрим, что это значит.
2. Ковариация
Ковариацию можно рассматривать как контракт на то, как принимается подтип, когда определен только супертип.
Давайте рассмотрим несколько основных примеров ковариации:
List extends Number> integerList = new ArrayList(); List extends Number> doubleList = new ArrayList ();
Таким образом, ковариация означает, что мы можем получить доступ к определенным элементам, определенным через их супертип . Однако нам не разрешается помещать элементы в ковариантную систему , так как компилятор не сможет определить фактический тип универсальной структуры.
3. Ковариантный Тип Возврата
Ковариантный тип возвращаемого значения – это то, что позволяет типу возвращаемого значения быть подтипом типа переопределенного метода .
Чтобы применить это на практике, давайте возьмем простой класс Producer с методом product () . По умолчанию он возвращает Строку в качестве Объекта , чтобы обеспечить гибкость для дочерних классов:
public class Producer { public Object produce(String input) { Object result = input.toLowerCase(); return result; } }
В результате Object в качестве возвращаемого типа мы можем иметь более конкретный тип возвращаемого значения в дочернем классе. Это будет ковариантный тип возврата и будет производить числа из последовательностей символов:
public class IntegerProducer extends Producer { @Override public Integer produce(String input) { return Integer.parseInt(input); } }
4. Использование структуры
Основная идея, лежащая в основе ковариантных типов возвращаемых данных, заключается в поддержке подстановки Лискова .
Например, давайте рассмотрим следующий сценарий производителя:
@Test public void whenInputIsArbitrary_thenProducerProducesString() { String arbitraryInput = "just a random text"; Producer producer = new Producer(); Object objectOutput = producer.produce(arbitraryInput); assertEquals(arbitraryInput, objectOutput); assertEquals(String.class, objectOutput.getClass()); }
После изменения на Integer Producer бизнес-логика , которая фактически дает результат, может остаться прежней:
@Test public void whenInputIsSupported_thenProducerCreatesInteger() { String integerAsString = "42"; Producer producer = new IntegerProducer(); Object result = producer.produce(integerAsString); assertEquals(Integer.class, result.getClass()); assertEquals(Integer.parseInt(integerAsString), result); }
Тем не менее, мы все еще ссылаемся на результат через объект . Всякий раз, когда мы начинаем использовать явную ссылку на Целочисленного производителя, мы можем получить результат в виде Целого числа без понижения:
@Test public void whenInputIsSupported_thenIntegerProducerCreatesIntegerWithoutCasting() { String integerAsString = "42"; IntegerProducer producer = new IntegerProducer(); Integer result = producer.produce(integerAsString); assertEquals(Integer.parseInt(integerAsString), result); }
Хорошо известный сценарий-это Объект# клон метод, который возвращает Объект по умолчанию. Всякий раз, когда мы переопределяем клон() метод, объект ковариантных типов возврата позволяет нам иметь более конкретный объект возврата, чем метод, объект ковариантных типов возврата позволяет нам иметь более конкретный объект возврата, чем метод, объект ковариантных типов возврата позволяет нам иметь более конкретный объект возврата, чем
метод, объект ковариантных типов возврата позволяет нам иметь более конкретный объект возврата, чем
метод, объект ковариантных типов возврата позволяет нам иметь более конкретный объект возврата, чем
метод, объект ковариантных типов возврата позволяет нам иметь более конкретный объект возврата, чем