如何判断一个对象是否实现了类似 toString() 的方法?

How to tell if an object implements a method like toString()?

我正在编写一个测试库,我希望能够处理可能未提供它们自己的 String toString() 实现的对象。

在那些情况下,我不想使用无用的默认实现,而是想使用 Apache Commons-Lang ToStringBuilder...但如果可用,我宁愿使用对象的 toString() 实现。

我如何判断一个对象是否有 toString() 的实现而不是默认的?

我发现的最好的方法是使用 java.lang.Class.getDeclaredMethod(String,Class<?>...) 并在对象的 class 层次结构上调用它,如下所示:

public static boolean objectHasToStringImplemented( final Object o ){
    return classHasToStringImplemented( o.getClass() );
}


public static boolean classHasToStringImplemented( final Class<?> initialClass ){

    Class<?> classToCheck = initialClass;
    while( classToCheck != Object.class ){
        if(  classImplementsToString( classToCheck )  ){
            return true;
        }

        classToCheck = classToCheck.getSuperclass();
    }

    return false;
}


private boolean classImplementsToString( final Class<?> aClass ){
    try{
        aClass.getDeclaredMethod( "toString" );
        return true;

    }catch( NoSuchMethodException e ){
        return false;

    }
}

然后我通过缓存答案而不触发异常来加快速度

//NB: implementation is synchronized and thus thread-safe
private static final Hashtable<Class<?>,Boolean> cachedAnswers = new Hashtable<>();
static {
    cachedAnswers.put( Object.class, Boolean.FALSE );
}

public static Boolean objectHasToStringImplemented( final Object o ){
    return classHasToStringImplemented( o.getClass() );
}


public static Boolean classHasToStringImplemented( final Class<?> classToCheck ){
    if( cachedAnswers.containsKey( classToCheck )  ){
        return cachedAnswers.get( classToCheck );
    }

    final Boolean result = classImplementsToString( classToCheck )  ||  classHasToStringImplemented( classToCheck.getSuperclass() );
    cachedAnswers.put( classToCheck, result );

    return result;
}

private static Boolean classImplementsToString( final Class<?> aClass ){
    for( Method m : aClass.getDeclaredMethods() ){
        if(  isToString( m )  ){
            return Boolean.TRUE;
        }
    }

    return Boolean.FALSE;
}


private static Boolean isToString( final Method m ){
    if(  m.getParameterCount() == 0  && m.getName().equals( "toString" )  &&  m.getReturnType().equals( String.class )  ){
        return Boolean.TRUE;

    }else{
        return Boolean.FALSE;
    }
}

我用以下方法测试过:


注意: 我没有用 default 方法实现测试这个,因为我目前无法访问 Java 8 JRE。