2016-02-06 4 views
3

У меня есть строфа кода, которая неоднократно используется. Вот два примера:Java - как передать метод в качестве параметра?

public java.sql.Struct createStruct(String typeName, Object[] attributes) { 
    String searchPath = getSearchPath(); 
    String name = setSearchPathToSchema(typeName); 
    Struct ret = delegate().createStruct(name.toLowerCase(), attributes); 
    setSearchPath(searchPath); 
    return ret; 
} 

public java.sql.Array createArray(String typeName, Object[] elements) { 
    String searchPath = getSearchPath(); 
    String name = setSearchPathToSchema(typeName); 
    Array ret = delegate().createArray(name.toLowerCase(), elements); 
    setSearchPath(searchPath); 
    return ret; 
} 

Вы можете видеть, что эти два метода имеют общий вид:

public <T> createXXX(String typeName, Object[] objects) { 
    String searchPath = getSearchPath(); 
    String name = setSearchPathToSchema(typeName); 
    T ret = delegate().createXXX(name.toLowerCase(), objects); 
    setSearchPath(searchPath); 
    return ret; 
} 

Где T является типом возвращаемого некоторого множества функций createXXX, которые имеют общую подпись, но разные возвращаемый тип.

Я уверен, что сделаю это в Javascript, F #, C#, Scala или любом другом языке.

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

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

Что-то вроде этого, где метод будет называться передается в качестве третьего параметра (я знаю, что этот код полностью уродливы в Java):

public Struct createStruct(String typeName, Object[] attributes) { 
    return createTypeWithCorrectSearchPath<>(
      typeName, 
      attributes, 
      delegate().createStruct 
); 
} 

public Struct createArray(String typeName, Object[] elements) { 
    return createTypeWithCorrectSearchPath<>(
      typeName, 
      elements, 
      delegate().createArray 
); 
} 

private <T> T createTypeWithCorrectSearchPath(
       String typeName, 
       Object[] objects, 
       [SOME-METHOD-HERE]) { 
    String searchPath = getSearchPath(); 
    String name = setSearchPathToSchema(typeName); 
    T ret = [SOME-METHOD-HERE](name, objects); 
    setSearchPath(searchPath); 
    return ret; 
} 

Я читал, видимо, дублирующие вопросы:

  1. How do I pass a method as a parameter in Java 8?
  2. How do I pass method as a parameter in Java?
  3. Java Pass Method as Parameter

Как и некоторые вопросы о ссылках метод с дженериков:

  1. Java 8 Method reference with generic types
  2. Java method reference to a method with generic parameter

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

UPDATE 2015-02-06

Хотя оба Louis Wasserman и Neuron дали в основном один и тот же ответ, он был Луи, что более педантично предложил модель, которая работала лучше для меня ... с явным примером того, как передать метод ссылка.

Вот что в конечном итоге работает для меня:

interface SearchPathCreator<T> { 
    T create(String typeName, Object[] objects) throws SQLException; 
} 

private <T> T createTypeWithCorrectSearchPath(
       String typeName, 
       Object[] objects, 
       SearchPathCreator<?> creator) throws SQLException { 
    String searchPath = getSearchPath(); 
    String name = setSearchPathToSchema(typeName); 
    //noinspection unchecked 
    T ret = (T) creator.create(name.toLowerCase(), objects); 
    setSearchPath(searchPath); 
    return ret; 
} 

, который называется так же, как Луис предложил:

public Struct createStruct(
       String typeName, 
       Object[] attributes) throws SQLException { 
    return createTypeWithCorrectSearchPath(
      typeName, 
      attributes, 
      delegate()::createStruct 
    ); 
} 

ответ

5
interface MyMethodType<T> { 
    T method(String name, Object[] objects); 
} 


private <T> T createTypeWithCorrectSearchPath(
      String typeName, 
      Object[] objects, 
      MyMethodType<T> impl) { 
    String searchPath = getSearchPath(); 
    String name = setSearchPathToSchema(typeName); 
    T ret = impl.method(name, objects); 
    setSearchPath(searchPath); 
    return ret; 
} 

createTypeWithCorrectSearchPath(typeName, objects, delegate()::createStruct); 

Решающим биты являются: а) создать свой собственный тип интерфейса, хотя Я полагаю, вы могли бы использовать BiFunction<String, Object[]>, b) используя ссылки на методы с ::.

+0

@ajb Вопрос OP, казалось, указывал на передачу в делегате() :: createStruct' как прекрасно, под частью «Что-то вроде этого». Я основывал свой ответ на этом предположении. Если бы у ОП не было такого примера, я бы переместил «Делегировать» в метод интерфейса. –

+0

Да, я просто подумал об этом ... – ajb

+0

Этот ответ дал мне самый дальний. Когда я использую этот шаблон дословно, ссылка метода 'delegate() :: createStruct' в' createTypeWithCorrectSearchPath' показывает 'Bad возвращаемый тип в ссылке метода: не может преобразовать' java.sql.Struct' в 'T'. Я обновил свой вопрос со структурой, которая закончилась работой. – rbellamy

2

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

public interface Creator<A>{ 
    A create(String name, Object[] attributes); 
} 

Затем определите свой метод, который принимает функцию типа вы только указанные:

public <A> A create(String typeName, Object[] attributes, Creator<A> creator){ 
    String searchPath = getSearchPath(); 
    String name = setSearchPathToSchema(typeName); 
    A ret = creator.create(name.toLowerCase(), attributes); 
    setSearchPath(searchPath); 
    return ret; 
} 

Наиболее удобный способ анонимных классов, где определяют интерфейсы реализация в строке:

java.sql.Struct struct = create("foo bar", new Object[]{"att1", "att2"}, new Creator<Struct>() { 
    @Override 
    public Struct create(String name, Object[] attributes) { 
     return //your implementation.. 
    } 
}) 

, если вы не можете поместить реализацию в анонимный класс, потому что вы должны получить доступ к delegate().createXXX(...), просто поставить Definitio n класса, реализующего интерфейс, в область, где ваш метод становится доступным.

2

Ответ Луи находится на правильном пути; однако мне не ясно, что delegate() будет доступен в точке, где вызывается метод create. Если он недоступен, вам понадобится метод с тремя аргументами.

Я не знаю, какой тип delegate() возвращает, но предположим, что это Delegate. Следует отметить, что если Delegate имеет метод экземпляра createArray(String s, Object[] objects), вы можете использовать Delegate::createArray в качестве ссылки на метод для функционального интерфейса для функции с тремя аргументами. Первым аргументом будет Delegate. Таким образом:

interface MyMethodType<T> { 
    T method(Delegate delegate, String name, Object[] objects); 
} 

Теперь в вашем методе createTypeWithCorrectSearchPath, вы могли бы назвать интерфейс, как это:

impl.method(delegate(), name.toLowerCase(), objects); 

Первый параметр вызова станет экземпляр, на котором два-аргумента экземпляра метод работает. То есть, если фактический параметр Delegate::createArray, она будет называться как

delegate().createArray(name.toLowerCase(), objects); 

В отличие от ответа Луи, вы должны определить свой собственный интерфейс здесь, потому что нет встроенного TriFunction класса в Java 8.

См. Section 15.13.3 of the JLS для полного описания того, как могут использоваться ссылки на методы. Этот конкретный приведен в параграфе, начинающемся «Если форма ReferenceType :: [TypeArguments] Идентификатор».

EDIT: После очередного изучения вопроса, я вижу, что createTypeWithCorrectSearchPath был предназначен для частного метода и не вызывался извне. Поэтому этот ответ, вероятно, неприменим. Я оставляю это здесь, потому что это может быть полезным ответом в некоторых подобных ситуациях.

+0

Хотя это не было специфично для моей текущей проблемы, это было очень полезно. – rbellamy

Смежные вопросы