我想创建一个适用于任何类的toString()方法,打印所有属性,就好像它的工作原理如下:
String toString(){
String s = "";
for(Attribute att: this.Attributes){
s += (att.name + ": " + att.toString() + "\n");
}
return s;
}
但是假设所有属性都有toString()方法,那么这就行了,所以让我们保留这个假设。
我想这个问题包含了反思的概念,我对这个概念知之甚少,也不知道如何在Java中应用它。
答案 0 :(得分:1)
我认为这是最简单的方法 - 它是lombok的注释@ToString,它默认会生成包含所有字段的toString()方法,但当然你可以排除其中的几个,或者只描述必需的字段。
https://projectlombok.org/features/ToString
看起来像
package test;
import lombok.AllArgsConstructor;
import lombok.ToString;
import java.util.Currency;
@AllArgsConstructor
@ToString
public class Example {
private String name;
private double amount;
private Currency currency;
public static void main(String[] args) {
Example example = new Example("visa", 1000, Currency.getInstance("USD"));
System.out.println(example.toString());
}
}
和输出
Example(name=visa, amount=1000.0, currency=USD)
答案 1 :(得分:1)
你可以通过反射来做到这一点,或者你可以使用像snakeyaml或gson或jackson这样的库。
你实际上可以序列化几个简单的对象,但是有些对象无法序列化为字符串,而且带有循环引用的对象也很难 stringify 。
你可以在java IDE中点击几下来做到这一点,例如在eclipse中有一个非常简单的向导,可以为任何对象执行此操作。
最后但并非最不重要的是,它不能很好地与java多态性一起使用,不建议让许多类继承共同的祖先只是为了覆盖toString()。
我个人坚持使用IDE简单的解决方案,这种解决方案既实用又不使用库,也不会对类别施加任何约束。
例如在eclipse中,您可以通过选择Object obj
:
Source > Generate toString ...
答案 2 :(得分:1)
是的,你是对的,这可以通过反思来实现。它已经在ReflectionToStringBuilder
中的Apache Commons中实现来自文档:
此方法的典型调用如下所示:
public String toString() { return ReflectionToStringBuilder.toString(this); }
但请注意潜在的性能问题 - 反思总会引入一些开销。
答案 3 :(得分:0)
这是一个相当复杂的事情,因为您需要处理的事情之一是循环引用,如果您不小心,将导致无限递归,从而导致StackOverflow异常。
这些是完成您想要的一系列方法:
public static String reflectionToString( Object obj )
{
StringBuilder stringBuilder = new StringBuilder();
reflectionToString( stringBuilder, obj );
return stringBuilder.toString();
}
public static void reflectionToString( StringBuilder stringBuilder, Object obj )
{
reflectionToString( new ArrayList<>(), stringBuilder, obj );
}
private static void reflectionToString( ArrayList<Object> visited,
StringBuilder stringBuilder, Object obj )
{
if( obj == null )
{
stringBuilder.append( "null" );
return;
}
Class<?> objectClass = obj.getClass();
if( objectClass == String.class )
{
StaticStringHelpers.appendEscapedForJava( stringBuilder, String.valueOf( obj ), '"' );
return;
}
if( objectClass.isPrimitive() )
{
stringBuilder.append( obj );
return;
}
if( Collection.class.isAssignableFrom( objectClass ) && ((Collection<?>)obj).isEmpty() )
{
stringBuilder.append( "{}" );
return;
}
if( objectClass.isEnum() )
{
stringBuilder.append( obj );
return;
}
if( objectClass.isArray() )
{
stringBuilder.append( objectClass.getComponentType().getName() ).append( "[]" );
}
else
{
stringBuilder.append( objectClass.getName() );
}
stringBuilder.append( "@" ).append( Integer.toHexString( System.identityHashCode( obj ) ) );
if( visited.contains( obj ) )
return;
visited.add( obj );
stringBuilder.append( "={" );
if( objectClass.isArray() )
{
for( int i = 0; i < Array.getLength( obj ); i++ )
{
if( i > 0 )
stringBuilder.append( "," );
Object val = Array.get( obj, i );
reflectionToString( visited, stringBuilder, val );
}
}
else
{
boolean[] first = { true };
reflectionToString( visited, stringBuilder, first, objectClass, obj );
}
stringBuilder.append( "}" );
}
private static void reflectionToString( ArrayList<Object> visited,
StringBuilder stringBuilder, boolean[] first,
Class<?> objectClass, Object obj )
{
Class<?> superClass = objectClass.getSuperclass();
if( superClass != null )
reflectionToString( visited, stringBuilder, first, superClass, obj );
Field[] fields = objectClass.getDeclaredFields();
AccessibleObject.setAccessible( fields, true );
for( Field field : fields )
{
if( Modifier.isStatic( field.getModifiers() ) )
continue;
appendNonFirst( stringBuilder, first, "," );
stringBuilder.append( field.getName() ).append( "=" );
try
{
Object fieldValue = field.get( obj );
reflectionToString( visited, stringBuilder, fieldValue );
}
catch( Exception e )
{
assert false : e;
}
}
}
private static StringBuilder appendNonFirst( StringBuilder stringBuilder,
boolean[] first, String text )
{
if( first[0] )
first[0] = false;
else
stringBuilder.append( text );
return stringBuilder;
}
ArrayList<Object> visited
收集所有访问过的对象,以避免重新访问它们。我不记得为什么它是一个ArrayList而不是Set。
StringBuilder
是收集字符串的地方。调用toString()
的{{1}}方法以reflectionToString()
结尾。
return stringBuilder.toString();
只是一个包含boolean[] first
的单元素数组,这是一种通过引用传递单个布尔值的hacky方法。它只是用于确定是否应该发出逗号。
boolean
是要发出的对象的类。
Class<?> objectClass
是要发出的对象。