7

I'm receiving an Object and need to log it. Some of them have cusom toString() and then I'd like to use that, but some have none and I get something like mightypork.rogue.bus.events.MouseMotionEvent@2d19587d.

I'm looking for a way to dynamically (perhaps with reflection?) check if the incoming object has toString() overridden itself.

String objToString(Object o)
{
    if(???) {
        return o.toString();
    } else {
        // fallback method
        return o.getClass().getSimpleName();
    }

}

Side note:

It's an event bus system, some classes can subscribe and then receive events based on implemented interfaces. I can't possibly require all clients to have toString(), plus I want to use this method for more than this one purpose.

Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
MightyPork
  • 18,270
  • 10
  • 79
  • 133
  • You should check if the method is overridden http://stackoverflow.com/questions/4821704/java-how-to-find-if-a-method-is-overridden-from-base-class – Marco Acierno Apr 04 '14 at 15:31
  • 1
    Well what would you do if they don't have a custom implementation anyway? You're not really supposed to do such a thing, it works around the purpose of polymorphism. – Joffrey Apr 04 '14 at 15:31
  • 2
    I don't care about principle of polymorfism, I want nice log output. – MightyPork Apr 04 '14 at 15:32
  • 2
    Then override `toString` yourself, that's what it's for! Don't you control your objects? – Joffrey Apr 04 '14 at 15:32
  • 1
    I'm don't want to add toString to everything; I don't have time for that. They aren't all data classes. – MightyPork Apr 04 '14 at 15:33
  • Anyway @Joffrey is right, so you will avoid the call of objToString and if in the future you will need the tostring of the object it's already here. – Marco Acierno Apr 04 '14 at 15:34
  • 1
    Which is all well and good, but it's far from clear how you could safely 'override' it if it didn't. The other issue is that to_string could have been overridden in a way that loses valuable information in terms of logging. – Tony Hopkinson Apr 04 '14 at 15:34
  • 1
    You can use `o.getClass().getDeclaredMethods()` to get all the methods you declared in the class. – helderdarocha Apr 04 '14 at 15:34
  • If the `toString()` is not among the methods returned by `getDeclaredMethods()` then its not overriden. – helderdarocha Apr 04 '14 at 15:36
  • In logging I'd want the class name for definite and then the value might be an optional extra. – Tony Hopkinson Apr 04 '14 at 15:37
  • @MightyPork You could use [`ToStringBuilder`](http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/builder/ToStringBuilder.html#reflectionToString%28java.lang.Object%29) to make the task a little less time consuming. – Duncan Jones Apr 04 '14 at 15:37
  • 1
    @MightyPork Do you mean you don't know what subset of your objects you want to log? Obviously not all classes need `toString` implementation, but not all of them need to be logged in console either. – Joffrey Apr 04 '14 at 15:37
  • 1
    @Joffrey It's an event bus system, some classes can subscribe and then receive events based on implemented interfaces. I can't possibly require all clients to have toString(), plus I want to use this method for more than this one purpose. – MightyPork Apr 04 '14 at 15:39
  • 1
    How about reading the docs on Reflection? http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getDeclaredMethods() – Germann Arlington Apr 04 '14 at 15:38
  • 2
    @MightyPork Ok that's why I asked "don't you control your objects?". So, since the answer is no, I then don't understand why you need more than the class name of the objects you get (in the case they do have a `toString`), because you don't know what the `String` will represent anyway. – Joffrey Apr 04 '14 at 15:48
  • I know I am late in the party, just a suggestion, if the function is not overridden, use Gson.toString()? Else do the same for everything, don't need to worry about the to String at all. :) – Bhaskar Feb 12 '18 at 06:52

4 Answers4

16

If you use the Object getClass() method, you can use the class and invoke the getMethod().getDeclaringClass() method. If it is Object, then it is not overridden. Otherwise, somewhere along the line is was overriden. Source

Community
  • 1
  • 1
nimsson
  • 930
  • 1
  • 14
  • 27
  • Trying this approach at the moment. – MightyPork Apr 04 '14 at 15:42
  • I haven't actually tested it, so it must work, right @MightyPork? – nimsson Apr 04 '14 at 15:55
  • 3
    Yes, it works pretty good. Basically `(o.getClass().getMethod("toString").getDeclaringClass() != Object.class)` is the thing I needed. – MightyPork Apr 04 '14 at 15:58
  • `getDeclaredMethods()` only returns methods declared in the class and **not** inherited. Is not that easier to use that? – Germann Arlington Sep 23 '14 at 14:08
  • 1
    @GermannArlington No, what if we have class `A` which overrides `toString` and we have class `B extends A`. If we would check if `B` did overwrite `toString` it would say `false` where the goal to check if it is not the `toString` from `Object`. – clankill3r Aug 02 '19 at 17:28
1

Maybe it is silly a bit but u can try that:

    String objToString(Object o)
{
    if(!(o.toString().contains("@" + o.hashCode()))) {
        return o.toString();
    } else {
        // fallback method
        return o.getClass().getSimpleName();
    }

}

or just something like that simplified version:

String objToString(Object o) {
  if (o.toString().equals(
      o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()))) {
    // fallback method
    return o.getClass().getSimpleName();
  } else {
    return o.toString();
  }
}
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
dharr
  • 328
  • 1
  • 11
  • could work, perhaps with a regex to be more precise, but there's got to be a cleaner way via reflection. – MightyPork Apr 04 '14 at 15:41
  • 1
    Yes, it is silly. You will exclude any `toString()` method that produces output which includes "@" in your suggested case and any other combination with any other substring – Germann Arlington Apr 04 '14 at 15:42
  • @GermannArlington Trivially extendable to `o.getClass().getName() + '@' + Integer.toHexString(o.hashCode())` as per the docs. – Duncan Jones Apr 04 '14 at 15:43
  • @GermannArlington ha im writing straight from my head – dharr Apr 04 '14 at 15:48
  • @Duncan The point is that your own implementation of `toString()` may include this information and if you use the code above even with improved searchString it will be excluded. So the only feasible option **is** using Reflection to check declared methods. – Germann Arlington Apr 04 '14 at 15:53
  • 3
    @GermannArlington If one checks for exactly `o.getClass().getName() + '@' + Integer.toHexString(o.hashCode())` and no more, then the matter is moot. Either no override has occurred or the override has chosen to present exactly the same information. – Duncan Jones Apr 04 '14 at 15:55
  • +0 - it's an ugly hack, but a working one, as @Duncan already pointed out. –  Mar 06 '16 at 16:56
  • This is incredibly unreliable. Default `toString()` is, iirc, implementation specific, so this will break depending on the JDK implementation you're using. Use only as an *absolute last resort* and prefer @nimsson's answer first. – Qix - MONICA WAS MISTREATED Jul 12 '16 at 09:52
1

Here to check if you have a toString method in your object :

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TemplateHelper {

    public static void main(String [] args){
        int a = 3;
        try {
            objToString(a);
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static void objToString(Object object) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException{
        Class c = object.getClass();
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            if(method.getName().equals("toString")){
                System.out.println(method.getName()); //THERE IS toString in object
                break;
            }
        }
    }

}
JajaDrinker
  • 652
  • 6
  • 15
1

Here's how you can use reflection to figure it out. Find the toString method in the object you received using reflection. If it was declared in java.lang.Object, then use your own method.

import java.lang.reflect.Method;
import java.util.Random;

public class ToStringUtils {
    public static String myToString(Object o) {
        if (o == null)
            return "null";
        if (overridesToString(o.getClass()))
            return o.toString();
        // Replacement for Object.toString()
        return o.getClass().getSimpleName() + "@" + System.identityHashCode(o);
    }

    public static boolean overridesToString(Class<?> clazz) {
        Method m;
        try {
            m = clazz.getMethod("toString");
        } catch (NoSuchMethodException e) {
            // Can't be thrown since every class has a toString method through Object
            return false;
        }
        return (m.getDeclaringClass() != Object.class);
    }

    // Small demo
    public static void main(String[] args) {
        System.out.println(myToString(new Integer(5)));
        System.out.println(myToString(new Random()));
    }
}
Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
  • Won't work correctly if parent class of current one overrides toString. But it's better then checking with regex as other suggestions – Bogdan Mart Aug 31 '22 at 09:52
  • 1
    @BogdanMart It works with overridden methods in parent classes in the way that the OP asked. What/when do you think doesn't work with it? – Erwin Bolwidt Sep 01 '22 at 00:41
  • I was wrong. I didn't noticed, that you check declaring class on fetched method. And I thought you are getting method from current class, not with inheritance. So nice, thanks. – Bogdan Mart Sep 01 '22 at 22:25