【问题标题】:How can I check if a String containing a qualified class name is an Exception class in java? [duplicate]java - 如何检查包含限定类名的字符串是否是java中的异常类? [复制]
【发布时间】:2021-08-09 20:32:24
【问题描述】:

我有以下代码:

@SuppressWarnings("unchecked")
private Class<Throwable> createClass(String className){
    try {
        Class<Throwable> convertedClass = (Class<Throwable>)Class.forName(className);
        return convertedClass;
    } catch(ClassNotFoundException | ClassCastException ex){
         LOG.error("Unable to convert exception class due to exception. Skipping this whitelisted exception class, {}. Please check your configurations.",className);
         LOG.error("Exception thrown while converting exception type.",ex);
         return null;
    }
}

本质上,我想确保转换后的类是一个例外。如果是,则返回该类,否则,返回 null。我认为通过返回Class&lt;Throwable&gt;,可以确保该类属于该类型,但它不是以这种方式工作的。如果我将java.lang.String 作为className 传递,我会得到Class&lt;String&gt; 而不是null。鉴于 Class 与 Class 不同,我预计会抛出 ClassCastException,但我认为泛型在这方面发挥了作用。有谁知道只返回 Throwable 类型的方法?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    Class&lt;Throwable&gt; convertedClass = (Class&lt;Throwable&gt;)Class.forName(className);

    编译器发出的警告会告诉你,这是非常危险的&lt;&gt; 中的位在强制转换操作的“类型”部分((SomeType&lt;T&gt;) expr 中的T)是断言。您是在告诉编译器假设它会是这种情况并停止抱怨它。 不进行运行时检查。因此,在运行时,绝对不能保证提供的类是Class&lt;Throwable&gt;。事实上,99.9999% 的可能性不是,因为 ONLY 字面上的 Throwable.class 是正确的类型;所有其他 throwable 最多只能是 Class&lt;? extends Throwable&gt;,因为泛型是不变的。

    因此,您最终会得到一个类型为 Class&lt;Throwable&gt; 的变量,但实际上并不包含该变量。对于类型系统的 -non-generics 部分,您不能以“错误”的方式结束,java 编译器和类验证器一起工作以保证 100%。但对于仿制药,这种保证是不可能的。你可以有一个包含字符串的List&lt;Number&gt;,没问题。

    结果是,与这种“错误”Class&lt;Throwable&gt; 类型交互的某些代码最终会得到 ClassCastException即使不涉及任何类型转换

    解决方法是永远不要在强制转换运算符中使用泛型,除了 &lt;?&gt; 之外,这在这个意义上不会出错,除非你真的知道自己在做什么。

    你想要的大概是这样的:

    Class<? extends Throwable> fixed =
      Throwable.class.asSubclass(Class.forName(className));
    

    正如javadoc of asSubclass 解释的那样,这将:

    1. 检查这里的参数 (Class.forName(className)) 是字面意义上的 Throwable.class,还是它的某个子类型。
    2. 如果否,则该调用不会正常完成,而是会抛出ClassCastException。如果你愿意,你可以抓住它。
    3. 如果是,则返回相同的类对象,但表达式更新为 Class&lt;? extends Throwable&gt; 类型。

    这为您节省了“丑陋的演员表”(产生警告的演员表)。

    一般提示:如果编译器发出警告而您没有得到它?您应该坚信您的代码已按设计破坏。寻找另一种方法,做一些研究,或尝试理解警告。编译器不会为了搞笑而在你脸上折腾。除非您非常确定自己确切知道自己在做什么,否则将警告视为错误。


    这会让你:

    private Class<? extends Throwable> createClass(String className) {
        try {
            return Throwable.class.asSubclass(Class.forName(className));
        } catch (ClassNotFoundException | ClassCastException e) {
                LOG.error("Unable to convert exception class due to exception. Skipping this whitelisted exception class, {}. Please check your configurations.", className);
                return null;
        }
    }
    

    注意:您的方法的返回类型从根本上被破坏了。它必须是Class&lt;? extends Throwable&gt;。我还删除了一条日志行——它似乎毫无用处。

    【讨论】:

      【解决方案2】:

      您可以使用Class#isAssignableFrom 来检查Class 是否为Throwable

      private Class<?> createClass(String className){
          try{
              Class<?> convertedClass = Class.forName(className);
              if(Throwable.class.isAssignableFrom(convertedClass)) return convertedClass;
          } catch(ClassNotFoundException ex){
              LOG.error("Unable to convert exception class due to exception. Skipping this whitelisted exception class, {}. Please check your configurations.",className);
              LOG.error("Exception thrown while converting exception type.",ex);
          }
          return null;
      }
      

      【讨论】:

      • asSubclass 是一个更好的选择。方法返回类型也可以改进很多。
      • 看我的例子。各方面都优越:更少的代码,更清晰的意图,更好的返回类型,不需要丑陋的演员。 (uglycast:导致编译器发出警告的一种)。这段代码也被主动破坏了:如果你传入一个可以加载的类,但不是异常类型的FQN,那么这段代码将默默地返回null。 CCEx 的捕获是没有用的,并且没有发出日志错误。看看这段代码有多糟糕?我休息一下。 Unmitigated 和您都没有清楚地发现错误。
      • @rzwitserloot 使用asSubclass时,演员阵容被简单地隐藏了。
      • @rzwitserloot 我认为使用isAssignableFrom 时意图更清晰。我仍然不同意这个答案“相当糟糕”。我其实更喜欢这个。
      • @rzwitserloot 这就是 OP 想要的。 “本质上,我想确保转换后的类是异常。如果是,则返回该类,否则,返回null。”
      猜你喜欢
      • 2017-09-03
      • 2011-08-13
      • 1970-01-01
      • 2010-11-12
      • 1970-01-01
      • 1970-01-01
      • 2017-10-21
      • 2011-06-23
      相关资源
      最近更新 更多