2015-05-27 4 views
3

Я пытаюсь сделать ссылку метода на метод, который имеет общий параметр, указанный в объявлении класса. Так у меня есть:Ссылка на метод Java с методом с общим параметром

public interface IExecutable<P extends IParameter> { 

    void execute(P parameter); 

} 

public class Parameter implements IParameter { 

    public void childSpecific() { 
    ... 
    } 
} 

public class TestClass { 
    ... 
    //somewhere in the code 
    public void foo(Parameter parameter) { 
     parameter.childSpecific(); 
    } 

    public void test() { 
     IExecutable<?> executable = this::foo; //compilation error 
     // The type TestClass does not define inner(IParameter) that is applicable here 
     executable.execute(new Parameter()); //compilation error as well 
     // The method execute(capture#4-of ?) in the type IExecutable<capture#4-of ?> is not applicable for the arguments (Parameter) 
    } 
    ... 
} 

Это специфичны, что я не знаю, конкретный общий тип исполняемого файла здесь. Использование

IExecutable<Parameter> = ... 

решает проблему немедленно, но это невозможно для случая.

Очевидно, что я делаю что-то неправильно. Но как заставить его работать?

Thx.

+0

ли '' IExecutable работы? Кроме того, я думаю, вы получаете множество предупреждений о недостающих общих типах. Попытайтесь исправить это. – Thilo

+3

Если у вас есть ссылка типа 'IExecutable ', вы не сможете вызывать 'execute' с чем-то другим, кроме литерала' null' в качестве аргумента. Поэтому я не понимаю, что вы пытаетесь сделать. –

+0

Существует важное различие между java7 и java8. В java7 это сработает. В Java8 это, очевидно, нет. –

ответ

3

В этом случае foo не записывается для обработки любых IParameter, кроме Parameter. Вы можете назначить ссылку на foo переменной типа IExecutable<? extends IParameter>, однако это означает, что это исполняемый файл, который обрабатывает неизвестный тип IParameter (в данном случае Parameter). Поскольку конкретный подтип неизвестен, было бы несинтетически безопасно передавать любой подтип IParameter в его метод выполнения, так как вы не знаете, с чем он может справиться в этой области!

Вам нужна другая переменная типа вместо использования захвата (?). Таким образом вы можете указать, что IParameter, с которым вы проходите, - это тот же тип, что и IParameter, который принимает исполняемый файл. Вы можете ввести этот новый метод, как я делаю ниже:

public class TestClass { 
    public static void foo(Parameter parameter) { 
    parameter.childSpecific(); 
    } 

    public static void main(String args) { 
    execute(TestClass::foo, new Parameter()); 
    } 

    public static <P extends IParameter> void execute(
     IExecutable<P> executable, P param) { 
    executable.execute(param); 
    } 
} 
+0

Большое спасибо, это именно то, что мне нужно (с добавлением, что методы и ссылки методов не были statc). – basme

0

Эта линия предоставляет сбой в умозаключения типа Java

IExecutable<?> executable = this::foo; 

Давайте посмотрим на этот путь

IExecutable<?> executable = p->this.foo(p); 

Чтобы скомпилировать его, java должен знать значение foo(p). До java8 тип выражения строится на типах подвыражений; здесь, тип p должен быть известен 1-й для разрешения foo. Но тип p не указан, он должен быть выведен из окружающего контекста. Здесь контекст IExecutable<? extends IParameter>, а p - IParameter - и метод foo(Iparameter) не существует.

В целом, вывод типа сталкивается с дилеммой, делает ли он вывод сверху вниз или снизу вверх? Java8 определяет чрезвычайно сложную процедуру, которая, которая по-человечески невозможно понять :)

Обходные: указать тип p

IExecutable<?> executable = (Parameter p)->this.foo(p); 

или задать более конкретный тип целевого

IExecutable<?> executable = (IExecutable<Parameter>)p->this.foo(p); 

IExecutable<?> executable = (IExecutable<Parameter>)this::foo; 

Если вы спросите дизайнеров языка, они подумают, что все это совершенно очевидно ... но лучшее действие программиста - это, вероятно, просто попробовать разные вещи, пока оно не сработает, чем изучить фактическую спецификацию языка.

+2

Даже с различными приведениями невозможно выполнить 'executable.execute (new Parameter())' с помощью исполняемого файла IExecutable . – Seelenvirtuose

+1

уверен, очевидно. – ZhongYu

1

Параметр типа P в вашем интерфейсе IExecutable ограничен типом IParameter. Рассмотрим эти два подтипа:

class Parameter implements IParameter { ... } 
class AnotherParameter implements IParameter { ... } 

Теперь IExecutable<?> не более конкретно в отношении указанного выше ограничения. Фактически, ? заявляет, что он связан с неизвестным подтипом IParameter, который может быть Parameter или AnotherParameter (в моем примере).

С таким объявлением переменной вы сталкиваетесь с двумя упомянутыми вами проблемами.

  1. Ваш метод foo(Parameter) не соответствует более общее ограничение на IExecutable<?>. Как видно выше, такой исполняемый файл может быть связан с AnotherParameter, что явно нарушит подпись метода foo.

  2. Даже если он соответствует, его нельзя использовать, как вы. Компилятор не знает, к какому типу фактически был сопоставлен ?. Единственное, что он знает: это должен быть подтип IParameter, но который неизвестен. Это означает, что заявление executable.execute(new Parameter()) не допускается (также как и executable.execute(new AnotherParameter())). Единственный параметр, который вы можете передать до execute, - null.

Вывод: Точка 1 может быть решена путем объявления переменной executable с типом IExecutable<? extends Parameter>. Это соответствует сигнатуре метода foo. Но точка 2 по-прежнему не позволяет звонить execute.

Единственное, что вы можете сделать, это объявить переменную как

IExecutable<Parameter> executable = this::foo; 

будет скомпилирован и разрешить вызов

executable.execute(new Parameter()); 
Смежные вопросы