6

I would like to know the class of a variable/property at runtime. For example:

Integer i = 5;

//pseudo-code
if (i.className == 'Integer') {
    System.debug('This is an integer.');
} else {
    System.debug('This is not an integer, but a ' + i.className);
}

I can't find the method/property that returns the class type in the documentation (assuming it's there). Am I missing it?

barelyknown
  • 5,510
  • 3
  • 34
  • 46
  • Voting for the idea [Method to get the Type of an Object -also Primitive Type not only SObject](https://success.salesforce.com/ideaView?id=08730000000l9wHAAQ) would be a good long term solution. – Daniel Ballinger Jun 03 '15 at 02:37

7 Answers7

11

From p. 122 of the Apex Developer Guide:

If you need to verify at runtime whether an object is actually an instance of a particular class, use the instanceof keyword...

But, you cannot use the instanceof keyword on an instance of the the class of its subclasses or else you'll receive a compile error. For example:

Integer i = 0;
System.debug(i instanceof Integer);

>> COMPILE ERROR: Operation instanceof is always true since an instance of Integer is always an instance of Integer.

You need to use the instanceof keyword on superclasses only. For example:

System.debug((Object)i instanceof Integer);

>> true

If you ever need information on type of the Class itself, check the System.Type methods (pg 396 of current Apex developer's guide. Here's are some examples:

Type integerType;
integerType = Type.forName('Integer');
integerType = Integer.class;
barelyknown
  • 5,510
  • 3
  • 34
  • 46
Adam
  • 2,595
  • 1
  • 16
  • 20
  • But how do I get type of an instance of a class (`i` in my example)? – barelyknown Mar 04 '12 at 12:36
  • 1
    Oh right, I don't think you can get primitives other than doing a brute-force try/catch block to verify string/numeric values. Apex isn't like Python where absolutely everything under the sun is an object. The Apex primitives need to be known and therefore need no checking. Every input/output has to be strictly typed, so there can never be any ambiguity as your code demonstrates (i is an Integer because it 'has' to be). But for any SObject you've got: if(sO.getsObjectType() == Account.sObjectType) and for custom Apex class instances you've got forName. – Adam Mar 04 '12 at 19:04
  • Let's imagine that a custom Apex class called Klass. I know that I can get the type of that class by calling Klass.class (or Type.forName('Klass'), but I still don't know a way to get the type for an **instance** of the class. Is there? – barelyknown Mar 04 '12 at 20:21
  • 1
    Sorry I misunderstood you. What you want is instanceOf. For example: if(someInstance instanceof Klass) ... (pg 122 of the Apex developer's guide) – Adam Mar 04 '12 at 20:43
  • Also, I don't believe there's a way to 'get the name' of a class instance, per se. But you can verify against a list of possibilities using instanceof, and glean the actual class name from that. – Adam Mar 04 '12 at 20:55
  • I did some research and updated your original answer. I'll accept it once you accept the edits. Thanks for helping me find the instanceof keyword (I remember now seeing it in the apex-commons package, but couldn't remember it for whatever reason). – barelyknown Mar 04 '12 at 21:42
  • This doesn't get you the type of an object however, it just lets you check if an object is of a known type. What is needed in Apex is a way to reliably get the type of any class such as C# `typeof()` – Shanerk Apr 11 '19 at 19:59
9

Someone asked me this earlier today. Here is a more dynamic solution.

MyClass mine     = new MyClass();
String myStr     = String.valueOf(mine);
String className = myStr.split(':')[0];
System.assertEquals('MyClass', className);
Phil R
  • 423
  • 4
  • 11
  • Since Apex doesn't allow a custom toString() method, this seems like an acceptable workaround for now for the custom classes. – haridsv Dec 31 '12 at 07:38
  • 1
    Salesforce seems to have an undocumented feature where you can override toString() in custom Apex classes. String.valueOf() will then return the String value returned in custom toString() implementation. – Alan Morey Apr 15 '14 at 20:23
2

A generic code of Salesforce Apex to explain :

public class Animal {}
public class Bird extends Animal {}

Animal a = new Bird();                      // Success
System.debug(a);                            // Bird:[]
System.debug((Bird)a);                      // Bird:[]
System.debug((Animal)a);                    // Bird:[]
System.debug(a instanceof Bird);            // true
System.debug((Animal)a instanceof Bird);    // true
System.debug((Bird)a instanceof Bird);      // Operation instanceof is always true since an instance of Bird is always an instance of Bird
System.debug((Bird)a instanceof Animal);    // Operation instanceof is always true since an instance of Bird is always an instance of Animal
Cray Kao
  • 573
  • 1
  • 8
  • 20
2

Just to add more information regarding toString() and String.valueOf().

String.valueOf() can not be relied upon to get the name of a Apex class at runtime. I don't think this is documented but if you add an toString() method to your custom class with the override keyword, then String.valueOf() will use that to determine the string value of your object.

class MyClassDefaultToString {
}

class MyClassOverrideToString {
    public override String toString() {
        return 'override toString';
    }
}

MyClassDefaultToString c1 = new MyClassDefaultToString();
System.assertEquals('MyClassDefaultToString:[]', String.valueOf(c1));

MyClassOverrideToString c2 = new MyClassOverrideToString();
System.assertNotEquals('MyClassOverrideToString:[]', String.valueOf(c2));
System.assertEquals('override toString', String.valueOf(c2));
Alan Morey
  • 121
  • 4
  • It doesn't appear to be documented, but overriding the inherited toString() method is used in an EXAMPLE in the [docs here](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_class_cache_Org.htm?search_text=toString) – Brent Bowers Aug 02 '17 at 18:45
1

This works for me

Map<String, Object> m =  (Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(myObjectInstance));
Map<String, Object> mComplexProperty = (Map<String, Object>)m.get('complexProperty');
String mSimpleProperty = (String)m.get('simpleProperty');

I'm not sure if the JSON serialization and deserialization will overhead, but... for now it's working

0

What if you need the FQCN instead of the class name (such as what you'd get from Java's getName())? Here is what you can do.

public class Beasts {
  public interface Animal {}
  public virtual class Bird implements Animal {}
  public class Robin extends Bird {}
}

System.assertEquals(
  Robin.class,
  Type.forName(ObjUtil.getName(new Robin(), true))
);
System.assertEquals(
  Robin.class,
  Type.forName(ObjUtil.getName((Animal)new Robin(), true)))
);
System.assertEquals(
  Robin.class,
  Type.forName(ObjUtil.getName((Bird)new Robin(), true)))
);

public class ObjUtil {
  public static String getName(Object obj, Boolean isQualifiedName) {
    // Qualified name
    if (isQualifiedName) {
      try {
        if (0 == (Long) obj) {
        }
      } catch (Exception ex) {
        typeName = ex.getMessage().remove('Invalid conversion from runtime type ').remove(' to Long');
        System.debug('typeName = ' + typeName);
        return typeName;
      }
    } else {
      // Class name
      typeName = String.valueOf(obj);
      if (typeName.indexOf(':[') != -1) {
        typeName = typeName.split(':')[0];
        System.debug('typeName = ' + typeName);
        return typeName;
      }
    }
  }
}
Shanerk
  • 5,175
  • 2
  • 40
  • 36
0

Not ideal, but here's a way to get this done:

public String getClassType(Object o)
{
    String retVal = 'Unknown';

    if(o instanceof Klass1)
        retVal = 'Klass1';
    else if(o instanceof Klass2)
        retVal = 'Klass2';
    else if(o instanceof Klass3)
        retVal = 'Klass3';

    return retVal;
} 
Adam
  • 2,595
  • 1
  • 16
  • 20