使用的方法引用具有返回类型Integer。但是String在下面的示例中,允许不兼容。
如何解决方法with声明以确保方法引用类型安全而无需手动强制转换?
import java.util.function.Function;
public class MinimalExample {
  static public class Builder<T> {
    final Class<T> clazz;
    Builder(Class<T> clazz) {
      this.clazz = clazz;
    }
    static <T> Builder<T> of(Class<T> clazz) {
      return new Builder<T>(clazz);
    }
    <R> Builder<T> with(Function<T, R> getter, R returnValue) {
      return null; //TODO
    }
  }
  static public interface MyInterface {
    Integer getLength();
  }
  public static void main(String[] args) {
// missing compiletimecheck is inaceptable:
    Builder.of(MyInterface.class).with(MyInterface::getLength, "I am NOT an Integer");
// compile time error OK: 
    Builder.of(MyInterface.class).with((Function<MyInterface, Integer> )MyInterface::getLength, "I am NOT an Integer");
// The method with(Function<MinimalExample.MyInterface,R>, R) in the type MinimalExample.Builder<MinimalExample.MyInterface> is not applicable for the arguments (Function<MinimalExample.MyInterface,Integer>, String)
  }
}
用例:类型安全但通用的Builder。
我尝试实现没有注释处理(自动值)或编译器插件(lombok)的通用生成器
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
public class BuilderExample {
  static public class Builder<T> implements InvocationHandler {
    final Class<T> clazz;
    HashMap<Method, Object> methodReturnValues = new HashMap<>();
    Builder(Class<T> clazz) {
      this.clazz = clazz;
    }
    static <T> Builder<T> of(Class<T> clazz) {
      return new Builder<T>(clazz);
    }
    Builder<T> withMethod(Method method, Object returnValue) {
      Class<?> returnType = method.getReturnType();
      if (returnType.isPrimitive()) {
        if (returnValue == null) {
          throw new IllegalArgumentException("Primitive value cannot be null:" + method);
        } else {
          try {
            boolean isConvertable = getDefaultValue(returnType).getClass().isAssignableFrom(returnValue.getClass());
            if (!isConvertable) {
              throw new ClassCastException(returnValue.getClass() + " cannot be cast to " + returnType + " for " + method);
            }
          } catch (IllegalArgumentException | SecurityException e) {
            throw new RuntimeException(e);
          }
        }
      } else if (returnValue != null && !returnType.isAssignableFrom(returnValue.getClass())) {
        throw new ClassCastException(returnValue.getClass() + " cannot be cast to " + returnType + " for " + method);
      }
      Object previuos = methodReturnValues.put(method, returnValue);
      if (previuos != null) {
        throw new IllegalArgumentException("Value alread set for " + method);
      }
      return this;
    }
    static HashMap<Class, Object> defaultValues = new HashMap<>();
    private static <T> T getDefaultValue(Class<T> clazz) {
      if (clazz == null || !clazz.isPrimitive()) {
        return null;
      }
      @SuppressWarnings("unchecked")
      T cachedDefaultValue = (T) defaultValues.get(clazz);
      if (cachedDefaultValue != null) {
        return cachedDefaultValue;
      }
      @SuppressWarnings("unchecked")
      T defaultValue = (T) Array.get(Array.newInstance(clazz, 1), 0);
      defaultValues.put(clazz, defaultValue);
      return defaultValue;
    }
    public synchronized static <T> Method getMethod(Class<T> clazz, java.util.function.Function<T, ?> resolve) {
      AtomicReference<Method> methodReference = new AtomicReference<>();
      @SuppressWarnings("unchecked")
      T proxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new InvocationHandler() {
        @Override
        public Object invoke(Object p, Method method, Object[] args) {
          Method oldMethod = methodReference.getAndSet(method);
          if (oldMethod != null) {
            throw new IllegalArgumentException("Method was already called " + oldMethod);
          }
          Class<?> returnType = method.getReturnType();
          return getDefaultValue(returnType);
        }
      });
      resolve.apply(proxy);
      Method method = methodReference.get();
      if (method == null) {
        throw new RuntimeException(new NoSuchMethodException());
      }
      return method;
    }
    // R will accep common type Object :-( // see /programming/58337639
    <R, V extends R> Builder<T> with(Function<T, R> getter, V returnValue) {
      Method method = getMethod(clazz, getter);
      return withMethod(method, returnValue);
    }
    //typesafe :-) but i dont want to avoid implementing all types
    Builder<T> withValue(Function<T, Long> getter, long returnValue) {
      return with(getter, returnValue);
    }
    Builder<T> withValue(Function<T, String> getter, String returnValue) {
      return with(getter, returnValue);
    }
    T build() {
      @SuppressWarnings("unchecked")
      T proxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, this);
      return proxy;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
      Object returnValue = methodReturnValues.get(method);
      if (returnValue == null) {
        Class<?> returnType = method.getReturnType();
        return getDefaultValue(returnType);
      }
      return returnValue;
    }
  }
  static public interface MyInterface {
    String getName();
    long getLength();
    Long getNullLength();
    Long getFullLength();
    Number getNumber();
  }
  public static void main(String[] args) {
    MyInterface x = Builder.of(MyInterface.class).with(MyInterface::getName, "1").with(MyInterface::getLength, 1L).with(MyInterface::getNullLength, null).with(MyInterface::getFullLength, new Long(2)).with(MyInterface::getNumber, 3L).build();
    System.out.println("name:" + x.getName());
    System.out.println("length:" + x.getLength());
    System.out.println("nullLength:" + x.getNullLength());
    System.out.println("fullLength:" + x.getFullLength());
    System.out.println("number:" + x.getNumber());
    // java.lang.ClassCastException: class java.lang.String cannot be cast to long:
    // RuntimeException only :-(
    MyInterface y = Builder.of(MyInterface.class).with(MyInterface::getLength, "NOT A NUMBER").build();
    // java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
    // RuntimeException only :-(
    System.out.println("length:" + y.getLength());
  }
}
          
                  为什么那是不可接受的?在第一种情况下,您不提供的类型
                
                  
                    —
                    Thilo 
                    
                  
                
              getLength,因此可以将其调整为返回Object(或Serializable)以匹配String参数。
                
                  我可能会弄错了,但是我认为您的方法
                
                  
                    —
                    GameDroids,
                    
                  
                
              with在返回时是问题的一部分null。当with()通过实际使用与参数R相同的函数类型来实现方法时,R会出现错误。例如<R> R with(Function<T, R> getter, T input, R returnValue) { return getter.apply(input); }
                
                  jukzi,也许你应该提供代码或关于你用的方法应该实际上做一个解释,为什么你需要
                
                  
                    —
                    sfiss 
                    
                  
                
              R是Integer。为此,您需要向我们展示如何利用返回值。似乎您想实现某种构建器模式,但是我无法识别一个通用模式或您的意图。
                
                  谢谢。我还考虑过检查完整的初始化。但是由于我在编译时看不到任何方法,因此我更喜欢使用默认值null / 0。我也不知道如何在编译时检查非接口方法。在运行时使用诸如“ .with(m-> 1).returning(1)”之类的非接口已经导致了早期的java.lang.NoSuchMethodException
                
                
                  
                    —
                    jukzi 
                    
                  
                
              
class代替a时是否一样interface?