Java泛型-在调用instanceof之后,有什么方法可以避免强制转换(和未经检查的警告)?

Java泛型-在调用instanceof之后,有什么方法可以避免强制转换(和未经检查的警告)?,第1张

概述Android代码-SharedPreferences类导出用于持久/检索不同首选项的不同方法:@SuppressWarnings("unchecked")publicstatic<T>Tretrieve(Contextctx,Stringkey,TdefaultValue){SharedPreferencesprefs=PreferenceManager.getDefaultSharedPreferences(ctx);

Android代码-SharedPreferences类导出用于持久/检索不同首选项的不同方法:

@SuppressWarnings("unchecked")public static <T> T retrIEve(Context ctx, String key, T defaultValue) {    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);    if (defaultValue instanceof Boolean) return (T) (Boolean) prefs            .getBoolean(key, (Boolean) defaultValue);    else if (defaultValue instanceof float) return (T) (float) prefs            .getfloat(key, (float) defaultValue);    // etc - another 4 cases}

这行得通,我可以调用booleanstored = retrIEve(ctx,“ BOolEAN_KEY”,true)-但是我的问题是:由于我已经使用过instanceof且T已简化为特定的类,因此有一种避免单双精度的方法演员和警告:未经检查?

编辑:如果我要通过该类中,我不妨调用getBoolean()或getfloat()等.我想要的是简化方法的内部结构,摆脱警告,但仍然能够调用retrIEve(ctx) ,“ KEY”,1或“ string”或true),然后获取我想要的内容

解决方法:

简短的回答:不,您不能摆脱警告.他们在那里是有原因的.

更长的答案:您可能已经知道,Java中的泛型只是语法糖加上编译时检查.几乎没有任何东西可以生存到运行时(称为“擦除”的过程).这意味着您方法中的(T)强制转换实际上是无 *** 作的.它将转换为最具体的类型,在这种情况下为Object.所以这:

(T) (Boolean) prefs.whatever()

真的变成这样:

(Object) (Boolean) prefs.whatever()

当然与以下内容相同:

(Boolean) prefs.whatever()

这可能会使您陷入危险境地,而警告正试图告诉您.基本上,您将丢失类型安全性,并且最终可能使您远离错误的实际位置(因此很难进行跟踪).想象以下情况:

// wherever you see "T" here, think "Object" due to erasurepublic <T> voID prefsToMap(String key, T defaultValue, Map<String, T> map) {    T val = retrIEve(this.context, key, defaultValue);    map.put(key, val);}Map<String,Integer> map = new HashMap<>();prefsToMap("foo", 123, map);// ... laterInteger val = map.get("foo");

到目前为止,一切都很好,并且在您的情况下也可以使用,因为如果“ foo”位于首选项中,则您将调用getInt来获取它.但是,请想象一下,如果您的检索函数中有一个错误,例如if(Integer的defaultValue instance)意外返回了getDouble()而不是getInt()(带有强制转换以及所有这些).编译器不会捕获它,因为您强制转换为T实际上只是强制转换为Object!在Integer val = map.get(“ foo”);之前,您不会发现它,它变为:

Integer val = (Integer) map.get("foo"); // cast automatically inserted by the compiler

此强制转换可能与错误真正发生的地方(getobject调用)相距很远,这使得很难进行跟踪. Javac试图保护您免受此侵害.

这是所有内容放在一起的示例.在此示例中,我将使用Number代替prefs对象,只是为了使事情保持简单.您可以复制粘贴此示例,然后按原样尝试.

import java.util.*;public class Test {    @SuppressWarnings("unchecked")    public static <T> T getNumber(Number num, T defaultVal) {        if (num == null)            return defaultVal;        if (defaultVal instanceof Integer)            return (T) (Integer) num.intValue();        if (defaultVal instanceof String)            return (T) num.toString();        if (defaultVal instanceof Long)            return (T) (Double) num.doubleValue(); // oops!        throw new AssertionError(defaultVal.getClass());    }    public static voID getInt() {        int val = getNumber(null, 1);    }    public static voID getLong() {        long val = getNumber(123, 456L); // This would cause a ClassCastException    }    public static <T> voID prefsToMap(Number num, String key, T defaultValue, Map<String, T> map) {        T val = getNumber(num, defaultValue);        map.put(key, val);    }    public static voID main(String[] args) {        Map<String, Long> map = new HashMap<String,Long>();        Long oneTwoThree = 123L;        Long fourFixSix = 456L;        prefsToMap(oneTwoThree, "foo", fourFixSix, map);        System.out.println(map);        Long fromMap = map.get("foo"); // Boom! ClassCastException        System.out.println(fromMap);    }}

注意事项:

>最大的优点:即使应该使用泛型来保护我,我还是得到了ClassCastException.不仅如此,我在一段代码中得到了错误,根本没有错误(主要).该错误发生在prefsToMap中,但是main支付了费用.如果映射是实例变量,则可能很难跟踪该错误的引入位置.
>除了使用数字而不是首选项外,我的getNumber与您的检索函数几乎相同
>我故意创建了一个错误:如果defaultVal是Long,我得到的是double(而不是long),并且强制转换为T.但是类型系统无法捕获此错误,这正是未经检查的强制转换试图警告我的内容(警告我它不能捕获任何错误,不一定有错误).
>如果defaultValue是int或String,则一切都会好起来.但是,如果它是Long类型,并且num为null,那么当呼叫站点希望使用Long类型时,我将返回Double这样的值.
>因为我的prefsToMap类仅强制转换为T(如上所述,它是无 *** 作强制转换),所以不会导致任何强制转换异常.在倒数第二行Long fromMap = map.get(“ foo”)之前,我不会例外.

使用javap -c,我们可以看到其中一些在字节码中的样子.首先,让我们看一下getNumber.请注意,对T的强制转换不会显示为任何内容:

public static java.lang.Object getNumber(java.lang.Number, java.lang.Object);  Code:   0:   aload_0   1:   ifnonnull   6   4:   aload_1   5:   areturn   6:   aload_1   7:   instanceof  #2; //class java/lang/Integer   10:  ifeq    21   13:  aload_0   14:  invokevirtual   #3; //Method java/lang/Number.intValue:()I   17:  invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;   20:  areturn   21:  aload_1   22:  instanceof  #5; //class java/lang/String   25:  ifeq    33   28:  aload_0   29:  invokevirtual   #6; //Method java/lang/Object.toString:()Ljava/lang/String;   32:  areturn   33:  aload_1   34:  instanceof  #7; //class java/lang/Long   37:  ifeq    48   40:  aload_0   41:  invokevirtual   #8; //Method java/lang/Number.doubleValue:()D   44:  invokestatic    #9; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;   47:  areturn   48:  new #10; //class java/lang/AssertionError   51:  dup   52:  aload_1   53:  invokevirtual   #11; //Method java/lang/Object.getClass:()Ljava/lang/Class;   56:  invokespecial   #12; //Method java/lang/AssertionError."<init>":(Ljava/lang/Object;)V   59:  athrow

接下来,看看getLong.注意,它将getNumber的结果强制转换为Long:

public static voID getLong();  Code:   0:   bipush  123   2:   invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;   5:   ldc2_w  #15; //long 456l   8:   invokestatic    #17; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;   11:  invokestatic    #13; //Method getNumber:(Ljava/lang/Number;Ljava/lang/Object;)Ljava/lang/Object;   14:  checkcast   #7; //class java/lang/Long   17:  invokevirtual   #18; //Method java/lang/Long.longValue:()J   20:  lstore_0   21:  return

最后,这是prefsToMap.请注意,由于它仅处理通用T类型(也称为对象),因此根本不进行任何转换.

public static voID prefsToMap(java.lang.Number, java.lang.String, java.lang.Object, java.util.Map);  Code:   0:   aload_0   1:   aload_2   2:   invokestatic    #13; //Method getNumber:(Ljava/lang/Number;Ljava/lang/Object;)Ljava/lang/Object;   5:   astore  4   7:   aload_3   8:   aload_1   9:   aload   4   11:  invokeinterface #19,  3; //InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;   16:  pop   17:  return
总结

以上是内存溢出为你收集整理的Java泛型-在调用instanceof之后,有什么方法可以避免强制转换(和未经检查的警告)?全部内容,希望文章能够帮你解决Java泛型-在调用instanceof之后,有什么方法可以避免强制转换(和未经检查的警告)?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1092891.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-28
下一篇 2022-05-28

发表评论

登录后才能评论

评论列表(0条)

保存