在Java中查找最特定的类

在Java中查找最特定的类,java,reflection,instanceof,Java,Reflection,Instanceof,我试图为我的特定目的编写某种异常处理程序。 我有一张班级名单。比如说: List<Class<? extends Throwable>> list = new LinkedList<>(); list.add(RuntimeException.class); list.add(IllegalArgumentException.class); List在这里试一试: public Class<? extends Throwable> findMos

我试图为我的特定目的编写某种异常处理程序。 我有一张班级名单。比如说:

List<Class<? extends Throwable>> list = new LinkedList<>();
list.add(RuntimeException.class);
list.add(IllegalArgumentException.class);
List在这里试一试:

public Class<? extends Throwable> findMostSpecific(Class<? extends Throwable> type) {
     // we'll keep a reference to the most specific one here
     Class<? extends Throwable> mostSpecific = null;
     // here we iterate over your list of types
     for (Class<? extends Throwable> tType : list) {
         // well not even a subtype of tType so ignore it
         if (!tType.isAssignableFrom(type)) {
             continue;
         }

         if (mostSpecific == null || mostSpecific.isAssignableFrom(tType)) {
             mostSpecific = tType;
         }
     }
     return mostSpecific;
}

public Class在这种情况下,您可以使用
Class.getCanonicalName()
在运行时检测当前类。考虑下面的示例用例,使用java流API找到最佳候选:

import org.junit.Test;

import java.util.LinkedList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class ReflectionTest {

    @Test
    public void test() {

        assertThat(findMostSpecific(IllegalArgumentException.class)).isEqualTo(IllegalArgumentException.class);

        assertThat(findMostSpecific(RuntimeException.class)).isEqualTo(RuntimeException.class);

        assertThat(findMostSpecific(IllegalStateException.class)).isEqualTo(RuntimeException.class);

        assertThat(findMostSpecific(IllegalStateException.class)).isEqualTo(RuntimeException.class);

        assertThat(findMostSpecific(CustomException.class)).isEqualTo(IllegalArgumentException.class);
    }

    public Class<? extends Throwable> findMostSpecific(Class<? extends Throwable> type) {
        List<Class<? extends Throwable>> list = new LinkedList<>();
        list.add(RuntimeException.class);
        list.add(IllegalArgumentException.class);

        return list.stream()
                .peek(e -> System.out.println("e.getClass() == " + e.getClass()))
                .filter(e -> type.getCanonicalName().equals(e.getCanonicalName()))
                .findAny()
                .orElseGet(() -> findMostSpecific((Class<? extends Throwable>) type.getSuperclass()));
    }

    public static class CustomException extends IllegalArgumentException {}
}

另一方面,
Class.getCanonicalName()
返回运行时类的规范名称
.orElseGet()
使用父类名称检查大多数特定类,因此对于
IllegalStateException.class
您将获得预期的
RuntimeException.class
。当然,这只是一个可以改进的示例代码(例如,不应该为每个方法调用实例化带有类的列表)。我希望它能有所帮助。

基于流的解决方案也可以由两个简单的步骤组成:

  • 根据元素是否可从给定类分配来过滤流
  • 计算剩余流的最大值
如图所示:

import java.util.Arrays;
import java.util.List;

public class MostSpecificClassFinder
{
    public static void main(String[] args)
    {
        List<Class<?>> classes = Arrays.asList(
            RuntimeException.class, 
            IllegalArgumentException.class
        );

        System.out.println(findMostSpecific(classes, IllegalArgumentException.class));
        System.out.println(findMostSpecific(classes, RuntimeException.class));
        System.out.println(findMostSpecific(classes, IllegalStateException.class));
        System.out.println(findMostSpecific(classes, CustomException.class));
    }

    public static Class<?> findMostSpecific(List<Class<?>> classes, Class<?> type) {
        return classes.stream()
            .filter(c -> c.isAssignableFrom(type))
            .max((c0, c1) -> c0.isAssignableFrom(c1) ? -1 : c1.isAssignableFrom(c0) ? 1 : 0)
            .get();
    }

}

class CustomException extends IllegalArgumentException 
{

}
导入java.util.array;
导入java.util.List;
公共类MOSTSSpecificClassFinder
{
公共静态void main(字符串[]args)
{
列表FindHostSpecific(列表类型){
返回类。stream()
.filter(c->c.isAssignableFrom(类型))
.max((c0,c1)->c0.isAssignableFrom(c1)?-1:c1.isAssignableFrom(c0)?1:0)
.get();
}
}
类CustomException扩展了IllegalArgumentException
{
}

只需使用表示类对象。通过
type.class
访问它,然后您可以使用
class#getName
之类的方法。从那里您还可以检查
class#isInstance(对象o)之类的内容
或调用方法或使用构造函数创建实例等。下面是示例。请注意,此类对象将始终在真实类上工作,而不是在受限视图上。因此,您可以直接收到您正在搜索的内容。很好的尝试,我在原问题中添加了一个要求,我认为在这种情况下它将不起作用。我稍后再试。它将完全按照您的预期工作-如果找不到类,则为父类调用
findMostSpecific
。因此
CustomException
将返回
IllegalArgumentException
,而不是
RuntimeException
。我已经更新了代码示例。两个答案都有效。很遗憾,我不能同时接受它们。Michael Rose比较快,所以我接受他的答案。还有+1。谢谢!有什么特别的原因可以通过
getCanonicalName
而不是直接通过类实例进行比较吗?比较规范名称不是在运行时比较类的好方法。有些类甚至没有规范名称(匿名、本地)和
getCanonicalName()
返回
null
,甚至可以由同名的不同类加载器加载两个类。我在原来的问题中又增加了一个要求。我必须检查它在这种情况下是否有效。你是说你的自定义类?应该仍然有效,因为你的自定义类不在列表中并且
 IllegalArgumentException
比RuntimeException更具体。它有效。第二个答案也有效,但因为你是第一个回答的人,我会很好地接受你的回答,但是你认为它比前面的例子好吗?只是问:)@Mariusz.v7诚然,我发现当前基于流的解决方案。。。“有点奇怪”,至少可以这么说:我看不出有什么理由通过类的
getCanonicalName
来比较类。除此之外,用递归调用结束流并再次对流进行操作看起来很奇怪。。。
e.getClass() == class java.lang.Class
e.getClass() == class java.lang.Class
e.getClass() == class java.lang.Class
import java.util.Arrays;
import java.util.List;

public class MostSpecificClassFinder
{
    public static void main(String[] args)
    {
        List<Class<?>> classes = Arrays.asList(
            RuntimeException.class, 
            IllegalArgumentException.class
        );

        System.out.println(findMostSpecific(classes, IllegalArgumentException.class));
        System.out.println(findMostSpecific(classes, RuntimeException.class));
        System.out.println(findMostSpecific(classes, IllegalStateException.class));
        System.out.println(findMostSpecific(classes, CustomException.class));
    }

    public static Class<?> findMostSpecific(List<Class<?>> classes, Class<?> type) {
        return classes.stream()
            .filter(c -> c.isAssignableFrom(type))
            .max((c0, c1) -> c0.isAssignableFrom(c1) ? -1 : c1.isAssignableFrom(c0) ? 1 : 0)
            .get();
    }

}

class CustomException extends IllegalArgumentException 
{

}