Тут проблема в том, что Predicate - это функциональный интерфейс, используемый для проверки условий. Т.е., у него один метод test(), который проверяет условие. Решение есть, оно довольно простое.
1) Создаём свой функциональный интерфейс, который принимает объект и возвращает строку:
interface Getter<T> {
String getString(T from);
}
Формально такой функциональный интерфейс соответствует лямбде, которая принимает один аргумент типа T. А такая лямбда, в свою очередь, соответствует ссылке на метод класса Т без аргументов.
2) Создаём join (delimeter я опустил, добавьте его сами):
public static <T> String join(Collection<T> collection, Getter<T> getter) {
StringBuilder builder = new StringBuilder();
for(T cursor: collection) {
builder.append(getter.getString(cursor));
}
return builder.toString();
}
3) Использовать так:
class User {
...
public String getName() { ... }
}
List<User> users = ...;
String joined1 = join(users, User::getName); //<-- так
String joined2 = join(users, user -> user.getName()); //<-- или так