Как передать ссылку на общий метод в качестве параметра? - PullRequest
0 голосов
/ 17 апреля 2019

Я хотел бы передать ссылку на общий метод в качестве параметра в метод Java.

Просто для примера я подготовил несколько абстрактных

public abstract class AbstractFoobar{

    abstract String getLiteral();
}

и 2 класса, которые его расширяют.Классы имеют конструктор параметров String.

public class Bar extends AbstractFoobar{

    String literal;

    public Bar(String literal) { this.literal = literal; }

    public String getLiteral() { return literal; }
}

public class Foo extends AbstractFoobar{

    String literal;

    public Foo(String literal) { this.literal = literal; }

    public String getLiteral() { return literal; }
}

У меня есть простой универсальный метод, который создает новый экземпляр объектов.Создание основано на параметре создателя функции

public <T extends AbstractFoobar> T foo(T foobar, Function<String, T> creator) {
    return creator.apply("foo" + foobar.getLiteral());
}

Этот пример отлично работает, когда метод foo выполняется с определенной ссылкой на метод Bar :: new.

@Test
void test()
    Bar bar = new Bar("bar");
    Bar foobar = foo(bar, Bar::new);
    assertEquals(Bar.class, foobar.getClass());
    assertEquals("foobar", foobar.getLiteral());
}

Но я не знаю, какпередать ссылочный метод через метод-обертку fooGenerator

public <T extends AbstractFoobar> T fooGenerator(T foobar) {
    //#1 return foo(foobar, T::new);
    //#2 return foo(foobar, foobar.getClass().getConstructor(String.class));        
}

# 1 Компилятор не может создать экземпляр типа T

# 2, метод foo (..., Function <>) не применим дляКонструктор аргумента <>

Ответы [ 2 ]

3 голосов
/ 17 апреля 2019

Очень важный, но никогда не подчеркиваемый момент о дженериках Java заключается в том, что - это просто причудливый способ избежать написания явных приведений . Они не больше, не меньше.

Итак, если вы не можете написать:

public AbstractFoobar fooGenerator(AbstractFoobar foobar) {
    return foo(foobar, /* something here, involving AbstractFoobar and casts */::new);
}

// Call site
Bar foobar = (Bar) foo(bar);

тогда вы не можете сделать это с помощью дженериков. И такого нет, потому что конструкторы не участвуют в наследовании: AbstractFoobar::new создаст экземпляр AbstractFoobar (если его можно создать), а не подкласс.

Для вашего текущего определения AbstractFoobar вы не можете сделать ничего лучше, чем просто вызвать foo напрямую с явными аргументами.


Единственный способ сделать это с одним параметром - это если у AbstractFoobar есть фабричный метод, например

public abstract class AbstractFoobar<A extends AbstractFoobar<A>> {
    // ...
    abstract A newInstance(String arg);
}

public class Bar extends AbstractFoobar<Bar> {
    // ...
    Bar newInstance(String arg) { return new Bar(arg); }
}

, который вы могли бы затем использовать как:

public <T extends AbstractFoobar<T>> T fooGenerator(T foobar) {
  return foo(foobar, foobar::newInstance);
}
0 голосов
/ 17 апреля 2019
Bar foobar = foo(bar, Bar::new);

допустимо, потому что вы ссылаетесь на конструктор для определенного класса.

Принимая во внимание:

foo(foobar, T::new)

пытается ссылаться на конструктор универсального типа.И это просто невозможно.Помните о стирании типов, в конце концов, здесь есть только Object и приведение типов.T::new просто не означает что-то значимое.

...