【问题标题】:How to initializing Class<T> at interface default method?如何在接口默认方法中初始化 Class<T>?
【发布时间】:2017-05-26 02:06:00
【问题描述】:

我有一个界面:

public interface ITransformer<S,T>{
    
    public void transform(S source,T target);
    
    default String getTransformerName(){
        Class<S> s;
        Class<T> t;
        
        return s.getName() + t.getName();  //*********
    }
    
}

星号行的错误信息:

局部变量s可能没有初始化

局部变量 t 可能尚未初始化

我想用这个方法返回一个带有 [S.classname][T.classname] 的字符串。请让我知道如何实现这一点,或者这在界面上是不可能做到的?

更新:1 月 12 日

我这样做的目的是因为这个类将在框架中,我想尽可能减少人为错误。我将代码更改如下:

public interface ITransformer<S,T>{
    
    public void transform(S source,T target);
    
    public FieldEntry<S, T> getTransformerName();
    
}


public class FieldEntry<S,T> implements Comparable<FieldEntry> {
    
    private Class<S> s;
    private Class<T> t;

    public FieldEntry(Class<S> s,Class<T> t){
        this.s = s;
        this.t = t;
    }
    
    public String getEntryName(){
        return s.getName() + t.getName();
        
    }

    @Override
    public int compareTo(FieldEntry entry) {
        if(entry == null) throw new IllegalArgumentException("The argument to compare cannot be null!");
        
        return entry.getEntryName().compareTo(this.getEntryName());
    }
    
}

【问题讨论】:

  • 没有接口也不可能。

标签: java generics interface java-8 default-method


【解决方案1】:

在 Java 中不可能得到 Class&lt;S&gt;,除非你已经知道 S 是哪个类,或者知道 S 是哪个类的其他东西给你一个。

【讨论】:

    【解决方案2】:

    这有点危险,我不会在生产中使用它(因为您应该在代码中涵盖接口的所有可能用例),但您可以使用反射:

    public interface ITransformer<S, T> {
    
        public void transform(S source, T target);
    
        default String getTransformerName() {
            Type[] genericInterfaces = this.getClass().getGenericInterfaces();
    
            ParameterizedType parameterizedType = null;
    
            for (Type genericInterface : genericInterfaces) {
                if (genericInterface instanceof ParameterizedType) {
                    ParameterizedType paramInterface = (ParameterizedType) genericInterface;
                    if (paramInterface.getRawType().equals(ITransformer.class)) {
                        parameterizedType = paramInterface;
                        break;
                    }
                }
            }
    
            if (parameterizedType == null) {
                throw new IllegalStateException("!");
            }
    
            return parameterizedType.getActualTypeArguments()[0].getTypeName() + parameterizedType.getActualTypeArguments()[1].getTypeName();  
    
        }
    }
    
    public class StringToIntegerTransfomer implements ITransformer<String, Integer> {
    
        @Override
        public void transform(String source, Integer target) {
    
        }
    }
    
    public interface StringToNumberTransfomer<T extends Number> extends ITransformer<String, T> {
    
    }
    
    public class StringToLongTransfomer implements StringToNumberTransfomer<Long>, ITransformer<String, Long> {
        @Override
        public void transform(String source, Long target) {
    
        }
    }
    
    @Test
    public void test() {
        ITransformer<String, Integer> intTransformer = new StringToIntegerTransfomer();
        ITransformer<String, Long> longTransformer = new StringToLongTransfomer();
        ITransformer<String, String> stringTransformer = new ITransformer<String, String>() {
    
            @Override
            public void transform(String source, String target) {
    
            }
        };
        ITransformer<String, Double> doubleTransformer = new StringToNumberTransfomer<Double>() {
    
            @Override
            public void transform(String source, Double target) {
    
            }
        };
    
        System.out.println(String.format("intTransformer: %s", intTransformer.getTransformerName()));
        System.out.println(String.format("longTransformer: %s", longTransformer.getTransformerName()));
        System.out.println(String.format("stringTransformer: %s", stringTransformer.getTransformerName()));
        System.out.println(String.format("doubleTransformer: %s", doubleTransformer.getTransformerName()));
    }
    

    此 sn-p 的输出:

    intTransformer: java.lang.Stringjava.lang.Integer
    longTransformer: java.lang.Stringjava.lang.Long
    stringTransformer: java.lang.Stringjava.lang.String
    
    
    java.lang.IllegalStateException: !
    

    此代码有一个限制,对于 ITransformer 的所有实现,您应该使用 implements ITransformer&lt;S, T&gt;。这就是为什么我有IllegalStateException 这条线ITransformer&lt;String, Double&gt; doubleTransformer = new StringToNumberTransfomer&lt;Double&gt;()。但是你可以改进这段代码。

    更好的选择是使用接口的一些基本实现并将源类和目标类传递给构造函数:

    public interface ITransformer<S, T> {
    
        void transform(S source, T target);
    
        String getTransformerName();
    }
    
    public abstract class BaseITransformer<S, T> implements ITransformer<S, T> {
    
        private final Class<S> sourceClass;
        private final Class<T> targetClass;
    
        public BaseITransformer(Class<S> sourceClass, Class<T> targetClass) {
            this.sourceClass = sourceClass;
            this.targetClass = targetClass;
        }
    
        public String getTransformerName() {
            return sourceClass.getName() + targetClass.getName();
        }
    }
    

    【讨论】:

    • 我想发布同样的事情...+1
    • 我敢打赌,OP 想通过 lambda 表达式来实现这个接口。然后,这不起作用。
    • 反射解决方案不仅对实现类施加了限制,而且类型参数始终为Classes 的假设增加了类型参数本身的限制。 ITransformer&lt;Set&lt;String&gt;,List&lt;String&gt;&gt;呢?
    • ITransformer&lt;Set&lt;String&gt;,List&lt;String&gt;&gt; 更新。对于 lambda 修复看起来更复杂,因为 java 返回 Class&lt;ITransformer&gt; 作为通用接口,而此类返回 Object 作为 getTypeParameters()[0].getBounds() 中的类型。同时,我在回答you should cover in your code all possible use cases of your interface 中说过,并建议使用第二个选项
    • 这是框架的一部分,其中源或目标不是集合。我想尽可能地简化实施者,以减少人为错误。令我惊讶的是,我的代码没有按预期工作
    【解决方案3】:

    为了说明为什么这行不通,你可以将你的班级改为

    public interface ITransformer<S,T>{
    
        public void transform(S source,T target);
    
        static <In,Out> ITransformer<In,Out> noOp() {
            return (source,target) -> {};
        }
        static void main(String... arg) {
            ITransformer<String,Integer> t1 = noOp();
            ITransformer<Long,Thread> t2    = noOp();
    
            System.out.println(t1 == (Object)t2);
        }
    }
    

    运行这将打印true。换句话说,两个函数都由相同的实例表示,因此不可能有允许识别它们不同类型的属性。

    通常,当两个函数(lambda 表达式或方法引用)表现出相同的行为时,JVM 可能会用相同的实现类型甚至相同的实例来表示它们。

    即使对于非接口类,由于类型擦除,这也不起作用。它仅在您有一个 reifiable(即非泛型)类型扩展或实现泛型类型时才有效。

    【讨论】:

    • 关于你的最后一句话:另一个答案正是通过 ParameterizedType 显示的。
    猜你喜欢
    • 1970-01-01
    • 2015-01-11
    • 2020-02-08
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    • 2021-11-20
    • 1970-01-01
    • 2022-01-19
    相关资源
    最近更新 更多