Is it possible to get the Type of an Object (it's not an SObject)?

String s = 'test';

public static void doSomething(o Object) {
    // need to find out if o is a string or an integer
    type = IsThereAMethodToGetTheTypeOf(o) // ?
    if(type='String') {
        // do something for strings here ....
    } else {
        // do something else ....

I think the Type class can't help me, because it has no way to get the type of an existing object instance, right? Any there any other way?

Since there is most likely no perfect solution possible within the current platform limitations, I posted an Idea on IdeaExchange here: https://success.salesforce.com/ideaView?id=08730000000l9wHAAQ

For SObject I have already this

public static string getType(SObject obj) {
    if(obj==null) return '';
    return obj.getSObjectType().getDescribe().getName()+'';
  • One silly (but actually usable) way I found is to use a couple of try/catches to iterate over possible types by typecast and assignment like try { Integer testInt = (Integer) o; ... } catch(Exception e) { try { String testStr = (String) o; ... } } - but can we do this better?
    – Uwe Heim
    Commented Aug 28, 2014 at 11:32
  • I can't even find any sfdc doc about 'Object' class :( Commented Aug 28, 2014 at 11:48
  • 2
    There is instanceof which is better than the casting approach though that also requires hard coding of the types. And it used to return true for null but I think that may now be fixed.
    – Keith C
    Commented Aug 28, 2014 at 12:20
  • @Uwe I'm curious why you need to know the Objects type? Run-time checking of an Object's type is usually an anti-pattern in most OO languages, see explanation.
    – Arpi Jakab
    Commented Dec 7, 2016 at 23:26
  • 1
    @ArpiJakab : For simple static stuff, I agree. But if you want to write Apps capable of working on different objects and fields dynamically, the anti-pattern is your only friend left.
    – Uwe Heim
    Commented Dec 9, 2016 at 11:14

7 Answers 7


Thanks to the comment of @KeithC I was able to go on with my research and as a result, it seems not possible to get a primitive data-type dynamically as a string. There are concepts to come close to it by the usage of instance of or try/catch in brute-force-like manner.

Since the primitive types are just a few, this workaround with instanceof seems acceptable for all types excepts of collections.

For list, map and set actually I was not able to find a satisfactory solution. Brute-force is not a general option, because of infinite possibilities. String.valueOf() can give a clue but not very much: for set and map, it returns a serialization wrapped in {} and for list it's wrapped in ().

Thanks to the feedback of @sfdcfox to try instanceof List<object>, map<object, object>, and set<object>, we seem to be able to detect lists. Unfortunately it does not work for maps and sets. This are my results:

object o;
o = new map<string,object>{}; 
system.debug( o instanceof map<string,object> ); // ==> true
system.debug( o instanceof map<object,object> ); // ==> false
o = new list<string>{}; 
system.debug( o instanceof list<string> ); // ==> true
system.debug( o instanceof list<object> ); // ==> true : only here it works
o = new set<string>{}; 
system.debug( o instanceof set<string> ); // ==> true
system.debug( o instanceof set<object> ); // ==> false

Any better approaches for collections are welcome!

The documentation to this topic is rare, as @mast0r said.

This links were helpful for me coming to this conclusion:

  • 1
    Seems ripe for an idea on idea exchange Uwe!
    – pchittum
    Commented Aug 28, 2014 at 14:06
  • 1
    instanceof List<object>, map<object, object>, and set<object> should return true for the respective data types, regardless of the parameters provided.
    – sfdcfox
    Commented Aug 28, 2014 at 14:11
  • @Peter here it is: success.salesforce.com/ideaView?id=08730000000l9wHAAQ ;-)
    – Uwe Heim
    Commented Aug 28, 2014 at 16:01
  • @sfdcfox : I updated this answer above with your suggestion of instanceof List<object>, map<object, object>, and set<object>. Unfortunately it does only work for List<object> which is a really inconsistent behavior of the platform - at least from my perspective. I added my current best-possible solution (which still has flaws) here as xs.getType(Object o). Thanks to your hint, it can now at least detect Lists (as just "List", not what kind of List<Kind>). If someone figures out how to catch Map and Set or how to figure Collection<Kind>, it would be awesome!
    – Uwe Heim
    Commented Aug 29, 2014 at 7:19
  • 2
    If somebody stumbles here - I was just able to detect a Set object by using a try/catch block: try { Iterable<Object> casted = (Iterable<Object>) o; isIterable = true; } catch (Exception e) {} if (isIterable && o instanceof List<Object>) { /* list */ } else { /* set and ... ? */ }. As you notice this dumb logic leaves the door open to other Iterable implementations, but it does the job for what I need (detecting Lists and Sets instances regardless their type)
    – MLucci
    Commented Nov 16, 2015 at 21:03


Here is my current Version. Now supports almost anything! Feedbacks for missing detections are welcome! The update is basically a combination between my earlier approach and the workaround provided by Arpi Jakab.

Be aware that the enhanced detection mechanism is based on parsing an exception message which is not free of risks and considered bad practice here and here. But I like it a lot!

public static String getTypeX(Object o) {
    if(o==null)                         return '';              // we can't say much about null
    String                              result  = 'Object';     // if we can't find out anything, we will return 'Object'
    if(o instanceof SObject)            return ((SObject)o).getSObjectType().getDescribe().getName()+''; 
    if(o instanceof Boolean)            return 'Boolean';
    if(o instanceof Id)                 return 'Id';
    if(o instanceof String)             return 'String';
    if(o instanceof Blob)               return 'Blob';
    if(o instanceof Date)               return 'Date';
    if(o instanceof Datetime)           return 'Datetime';
    if(o instanceof Time)               return 'Time';
    if(o instanceof Integer)            return 'Integer';
    if(o instanceof Long)               return 'Long';
    if(o instanceof Decimal)            return 'Decimal';
    if(o instanceof Double)             return 'Double';
    if(o instanceof List<object>)       return 'List';
    try {                                                       // crazy but powerful workaround based on the answer of Arpi Jakab (20217-04-17) on SFSE ==> https://salesforce.stackexchange.com/questions/48019/how-to-get-the-runtime-type-of-an-object-dynamically-for-primitive-data-types-a 
        Name a = (Name) o;                                      // INTENTIONALLY force runtime exception like: "Invalid conversion from runtime type Map<Id,List<Account>> to Name"
    } catch (Exception e) {
        result                          = e.getMessage().substringAfter('from runtime type ').substringBefore(' ');
    return result;


The closest I could figure out with the approaches that we have right now you see below. Note that as an unfortunate we still can't detect sets and maps and we can't distinguish between decimal and double. The rest feels usable.

public class xs {
  public static string getType(Object o) {
    if(o==null) return '';              // we can't say much about null with our current techniques
    if(o instanceof SObject)            return ((SObject)o).getSObjectType().getDescribe().getName()+''; 
    if(o instanceof Boolean)            return 'Boolean';
    if(o instanceof Id)                 return 'Id';
    if(o instanceof String)             return 'String';
    if(o instanceof Blob)               return 'Blob';
    if(o instanceof Date)               return 'Date';
    if(o instanceof Datetime)           return 'Datetime';
    if(o instanceof Time)               return 'Time';
    if(o instanceof String)             return 'String';
    if(o instanceof Integer)            return 'Integer';
    if(o instanceof Long)               return 'Long';
    if(o instanceof Decimal)            return 'Decimal';  // we can't distinguish between decimal and double
    if(o instanceof Double)             return 'Double';   // we can't distinguish between decimal and double
    if(o instanceof List<object>)       return 'List';
    return 'Object';                    // actually we can't detect maps and sets and maps

Actually I'm not that kind of big test-writer, but this could be rewritten and used as test. I run it as Execute Anonymous to verify the results:

list<string>t00 = new list<string>{'test'};             system.debug('List<string> : '  + xs.getType(t00));
Account     t01 = new Account();                        system.debug('Account : '       + xs.getType(t01));
Boolean     t02 = true;                                 system.debug('Boolean : '       + xs.getType(t02));
Boolean     t03 = false;                                system.debug('Boolean : '       + xs.getType(t03));
String      t04 = 'sdfsdf';                             system.debug('String  : '       + xs.getType(t04));
Id          t05 = [select id from user limit 1][0].Id;  system.debug('Id : '            + xs.getType(t05));
Blob        t06 = Blob.valueOf('testsdf');              system.debug('Blob : '          + xs.getType(t06));
Datetime    t07 = Datetime.now();                       system.debug('Datetime : '      + xs.getType(t07));
Time        t08 = Time.newInstance(18, 30, 2, 20);      system.debug('Time : '          + xs.getType(t08));
Date        t09 = (Date) Date.today();                  system.debug('Date : '          + xs.getType(t09));
Integer     t10 = 7;                                    system.debug('Integer : '       + xs.getType(t10));
Decimal     t11 = 18.99;                                system.debug('Decimal : '       + xs.getType(t11));
Double      t12 = 77.99;                                system.debug('Double : '        + xs.getType(t12));
Long        t13 = 9;                                    system.debug('Long : '          + xs.getType(t13));
  • It's quite obvious but, if you implement something like this, note that order of the lines above is important to get proper results returned.
    – Uwe Heim
    Commented Aug 28, 2014 at 15:28
  • Also instanceof still returns true for null as of today (2014-08-28)
    – Uwe Heim
    Commented Aug 28, 2014 at 15:31
  • You can detect sets and maps using overloading. And also distinguish Double from Decimal. Added an answer as the code's too long to fit in a comment.
    – Dominic
    Commented Aug 28, 2014 at 17:55
  • To add to Uwe Heim's comment: "instanceof sObject" appears to return true for a List<sObject> -- so I believe the "if(o instanceof List<object>)" line should come first Commented Dec 9, 2015 at 1:07
  • Curiously, Integer is now getting picked up as a string. Commented Nov 9, 2019 at 20:20


Here's the code I was actually looking for that does precisely what you wanted:

private string returnType( Object whatTypeAmI )
   String name = 'undefined';

   If( whatTypeAmI instanceof Integer )
       name = 'Integer';
   else if( whatTypeAmI instanceof Double)
      name = 'Double';
   else if( whatTypeAmI instanceof String)
      name = 'String';
   else if( whatTypeAmI instanceof Blob)
      name = 'Blob';
   else if( whatTypeAmI instanceof Boolean)
      name = 'Boolean';
   else if( whatTypeAmI instanceof Date)
      name = 'Date';
   else if( whatTypeAmI instanceof Datetime)
      name = 'DateTime';
   else if( whatTypeAmI instanceof Decimal)
      name = 'Decimal';
   else if( whatTypeAmI instanceof ID)
      name = 'Id';
   else if( whatTypeAmI instanceof Long)
      name = 'Long';
   else if( whatTypeAmI instanceof Time)
      name = 'Time';


Here's some old code I have I believe you could utilize for your code block to call from a class that I believe would serve the purpose of what you're looking for; particularly by using the boolean methods of the original test class. Using those, you wouldn't even need a try-catch block, just us If statements. When you finally get a "true" that's returned, you're done!

Using this method would of course require that you have data that isn't null in the list[0] or set[0] location of the collection you want to test. Potentially there could be an issue with a value of 0. Is it an integer, a decimal or a string? For maps, I'm not certain how helpful this would be, but you're a creative guy. Hopefully this will help point you towards a creative solution that will work for you.

Because I would like to directly call my Converter's class methods without creating a new instance of the class, I have defined all the methods as "static".

The methods for this class are:

  • ToString(Integer)
  • ToString(Double)
  • ToString(Long)
  • ToString(Boolean)
  • ToString(Date)
  • ToString(Date,format) sample: zConvert.ToStrong(mydate,'MM-dd-yy')
  • ToString(Time)
  • ToString(Time,format) sample: zConvert.ToStrong(myTime,'hh:mm:ss')
  • ToString(Datetime)
  • ToString(Datetime,format)
  • ToString(Decimal)
  • ToString(Decimal, ScientificNotaion) ScientificNotaion is a Boolean value and if false is passed then the string will not have scientific notations.
  • FileSizeToString(Long) Returns values such as "5.5 KB", "8 MB", etc. Parameter passed is in bytes.
  • CurrencyToString(Decimal, CurrencyChar) CurrencyChar can be "$", "£", etc

Here's the actual class:

   public class zConvert{

    // The Initial Developer of the Original Code is Sam Arjmandi. Portions created by
    // the Initial Developer are Copyright (C) 2008 the Initial Developer. All Rights Reserved.  
    // This Code is provided "As Is" without warranty of any kind.  

    public static String ToString(integer Value) {     
        /* string representation if an Integer value */     
        return Value.format(); 

    public static String ToString(Double Value) {   
        /* string representation if a Double value */
        return Value.format(); 

    public static String ToString(Boolean Value) {
        /* string representation if a Boolean value */    
        if (Value)      return 'true';
        else  return 'false'; 

    public static String ToString(Long Value) {
        /* string representation if a Long value */   
        return Value.format(); 

    public static String ToString(Date Value) {    
        /* string representation if a Date value */    
        return Value.format(); 

    public static String ToString(Date Value,String format) {
        /* string representation if a Date value with formatting */   
        Datetime temp = Datetime.newInstance(Value.year(), Value.month(), Value.day());   
        return temp.format(format); 

    public static String ToString(Datetime Value) {
        /* string representation if a Datetime value */    
        return Value.format(); 

    public static String ToString(Datetime Value,String format) {    
        /* string representation if a Datetime value with formatting */
        return Value.format(format);

    public static String ToString(Time Value) {
        /* string representation if a Time value */   
        return String.valueOf(Value); 

    public static String ToString(Time Value, String format) {
        /* string representation if a Time value with formating */
        Datetime temp = Datetime.newInstance(1970, 1, 1, Value.hour(), Value.minute(), Value.second());   
         return temp.format(format); 

    public static String ToString(Decimal Value) {
         /* string representation if a Decimal value */   
         return Value.format(); 

    public static String ToString(Decimal Value, Boolean ScientificNotation) {
          /* string representation if a Decimal value with or without Scientific Notation */   
         if (ScientificNotation)    return Value.format();
         else    return Value.toPlainString(); 

    public static String FileSizeToString(Long Value) {

       /* string representation if a file's size, such as 2 KB, 4.1 MB, etc */
       if (Value < 1024) return ToString(Value) + ' Bytes';
         else if (Value >= 1024 && Value < (1024*1024))    {
         Decimal kb = Decimal.valueOf(Value);      
         kb = kb.divide(1024,2);
         return ToString(kb) + ' KB';    
         }    else    if (Value >= (1024*1024) && Value < (1024*1024*1024))    {      
         Decimal mb = Decimal.valueOf(Value);      
         mb = mb.divide((1024*1024),2);      
         return ToString(mb) + ' MB';    
         }    else    {      
         Decimal gb = Decimal.valueOf(Value);      
         gb = gb.divide((1024*1024*1024),2);          
         return ToString(gb) + ' GB';    

    public static String CurrencyToString(Decimal Value, String CurrencyChar) {    
         return CurrencyChar + ToString(Value); 

Here's how to get 100% test coverage:

/* test string utils */
system.assertEquals('4/17/1960',zConvert.ToString(date.newInstance(1960, 4, 17)));
system.assertEquals('Apr, 26 04 11:24:40',zConvert.ToString(datetime.newInstance(2004, 4, 26, 23, 24, 40), 'MMM, dd yy hh:mm:ss'));
system.assertEquals('Apr, 17 1960',zConvert.ToString(date.newInstance(1960, 4, 17), 'MMM, dd yyyy'));
system.assertEquals('4/26/2004 11:24 PM',zConvert.ToString(datetime.newInstance(2004, 4, 26, 23, 24, 40)));
system.assertEquals('0',zConvert.ToString(decimal.valueOf('.000000000000000000000012'), true));
system.assertEquals('12.4567',zConvert.ToString(decimal.valueOf('12.4567'), false));
system.assertEquals('18:30:02.020Z',zConvert.ToString(time.newInstance(18, 30, 2, 20)));
system.assertEquals('06-30-02-302',zConvert.ToString(time.newInstance(18, 30, 2, 20), 'hh-mm-ss-ms'));
system.assertEquals('$123,456.17',zConvert.CurrencyToString(decimal.valueOf('123456.17'), '$'));
system.assertEquals('1,023 Bytes',zConvert.FileSizeToString(long.valueOf('1023')));
system.assertEquals('1,015.62 KB',zConvert.FileSizeToString(long.valueOf('1040000')));
system.assertEquals('1,020.43 MB',zConvert.FileSizeToString(long.valueOf('1070000000')));
system.assertEquals('1,015.14 GB',zConvert.FileSizeToString(long.valueOf('1090000000000')));
  • Thanks @crmprogdev, that's the way we have to go right now, I guess. Pretty much what I came up in the end. But also no full solution for set, map and list. Did you check, if your code differs between double and decimal? That I couldn't get either. I also put in the solution for SObject, so we have a single method for both use cases.
    – Uwe Heim
    Commented Aug 28, 2014 at 16:07
  • Yes there's an instance of Double in the WhatTypeAmI class. Its also accounted for in the zConvert code as well. Ditto for longs. It took me a while to find the code in my archives, thus the reason you didn't get the Edit portion first as that's what I was looking for initially.
    – crmprogdev
    Commented Aug 28, 2014 at 16:26
  • I've seen the "instanceof double" in your code, but actually I doubt that it can work. It probably returns always the result, which you try to detect first. But correct me, if I'm wrong.
    – Uwe Heim
    Commented Aug 28, 2014 at 16:31
  • I got the whatTypeAmI code from a very experienced and established developer in our SFDUG quite some time ago who was very helpful to me when I was getting started. I've not specifically tested it against a double. I can only say that I'd trust any code he's given me. I recommend you test it yourself to see if it reliably returns the results you expect.
    – crmprogdev
    Commented Aug 28, 2014 at 17:27
  • thx will do so.
    – Uwe Heim
    Commented Aug 28, 2014 at 17:29

To read why this is typically a bad practice read here and here.

Now here is the rope to hang yourself :p

Map<List<String>, Decimal> x = new Map<List<String>, Decimal>();
Object o = x;

String theType = 'Account'; 
try {
    Account a = (Account)o; 
} catch (Exception e) {
    // MSG = 'Invalid conversion from runtime type [OBJECT TYPE] to Account'
    String m = e.getMessage();
    String[] tokens = m.split(' ');
    for (Integer i = 0; i < tokens.size(); i++) { 
        if (tokens[i] == 'type') {
            theType = tokens[i + 1];
System.debug('Type is ' + theType); // prints 'Type is Map<List<String>,Decimal>'

I make no guarantees that this code works across all Salesforce versions and it may break any time Salesforce modifies the exception error message. You've been warned!


The platform can distinguish between all these types, so if we accept that we're going to be coding a list and matching against it, we can do it this way. Let overloading sort it out instead of trying InstanceOf on an Object. This class distinguishes between Double and Decimal, and can detect any map or list I've thrown at it. I'd use statics IRL but this runs nicely in Execute Anonymous.

class TestType
    public string whatAmI(Double d)
        return 'I am a Double';

    public string whatAmI(Decimal d)
        return 'I am a Decimal';

    public string whatAmI(map<Id,list<Account>> d)
        return 'I am a map of Ids to lists of Accounts';

TestType tt = new TestType();
Decimal dec = 123.456;
Double dub = 789.987;
map<Id, list<Account>> prettySpecificMap = new map<Id, list<Account>>();

system.debug('A decimal? ' + tt.whatAmI(dec));
system.debug('A double? ' + tt.whatAmI(dub));
system.debug('Something very specific? ' + tt.whatAmI(prettySpecificMap));
  • Interesting approach! Due to the infinite possibilities it has the same flaws for maps, sets and list as the instanceof-approach - you can't write one single method in an utility class, which is reusable for each and every use case without modification. But to distinguish between Decimal and Double it would be usable in a general solution.
    – Uwe Heim
    Commented Aug 28, 2014 at 18:08
  • Thats awesome. You cant use Map<Object, Object> with this approach as a catch all?
    – Phil Rymek
    Commented Aug 28, 2014 at 18:08
  • 1
    whatAmI(map<object, object> d) doesn't seem viable. I get "method doesnt exist or incorrect signature" in what you'd think of as a catch-all case. Each specific map does have to be defined individually. Although map<Id,SObject> does work. And then you can use SObject describe to find out what it actually is.
    – Dominic
    Commented Aug 28, 2014 at 18:19
  • 1
    I tried this way but beside of the differentiation between Decimal and Double I found it less flexible. Having a general reflectional solution in mind, the biggest disadvantage for me is that you can't use it to test an instance of Object of being something more specific like String or Accounts dynamically during runtime. Say you have a mixed List<object> and want to process each element according to it's real type. So I would stick with xs.getType(Object o) defined in my answer and live with it's flaws until someone or Salesforce show us how to do it better.
    – Uwe Heim
    Commented Aug 29, 2014 at 7:07
  • 1
    This is entirely useless, and drastically inferior to the instanceof version. The decision about which version of an overloaded method to call happens at compile time, not runtime. Consequently: 1) you can't gain any information about types that isn't already known at compile time from your variable declarations or method return types, and 2) if you declare whatAmI variants for compatible types (like String and Object) you can get different results for the exact same object depending upon how you cast it.
    – Mark Amery
    Commented Feb 28, 2015 at 19:51

I know this question has been answered but I have another solution that does support collections of any type.

public static String getObjectType(Object obj){

    String result = 'Date';
        Date typeCheck = (Date)obj;
    catch(System.TypeException te){

        String message = te.getMessage().substringAfter('Invalid conversion from runtime type ');
        result = message.substringBefore(' to Date');

    return result;
  • 1
    You can cast Date to Datetime, so this method yields incorrect type for any Date. In your example, it would be better to use Date instead of Datetime, since it's not possible to cast Datetime to Date and hence. Commented Sep 13, 2018 at 18:40
  • 1
    This is basically the same as the solution from @ArpiJakab posted 2 years before.
    – Phil W
    Commented May 29, 2020 at 8:12

Here a full version with primitive types and Object one.

public with sharing class RuntimeHelper 
    public static String whatAmI(String me) {
        return 'String';

    public static String whatAmI(Id me) {
        return 'Id';

    public static String whatAmI(Boolean me) {
        return 'Boolean';
    public static String whatAmI(Blob me) {
        return 'Blob';
    public static String whatAmI(Date me) {
        return 'Date';
    public static String whatAmI(Datetime me) {
        return 'Datetime';
    public static String whatAmI(Integer me) {
        return 'Integer';

    public static String whatAmI(Decimal me) {
        return 'Decimal';
    public static String whatAmI(Double me) {
        return 'Double';

    public static String whatAmI(Object me) {
        String  runTimeType  = 'Object';    
        runTimeType  = (me instanceof SObject) ? ((SObject)me).getSObjectType().getDescribe().getName()+'' : runTimeType;        
        return runTimeType;

Test class

public with sharing class RuntimeHelperTest {

    static void runtimeHelperTest() {

        String returnedType = '';
        String stringType = 'I am a String';
        returnedType = RuntimeHelper.whatAmI(stringType);
        System.assert(returnedType.equals('String'),'I am not a String');     

        Boolean booleanType = false;
        returnedType = RuntimeHelper.whatAmI(booleanType);
        System.assert(returnedType.equals('Boolean'),'I am not a Boolean');
        Blob blobType;
        returnedType = RuntimeHelper.whatAmI(blobType);
        System.assert(returnedType.equals('Blob'),'I am not a Blob');
        Date objDate = Date.TODAY();
        returnedType = RuntimeHelper.whatAmI(objDate);
        System.assert(returnedType.equals('Date'),'I am not a Date');

        Datetime myDateTime = Datetime.newInstance(1960, 2, 17);
        returnedType = RuntimeHelper.whatAmI(myDateTime);
        System.assert(returnedType.equals('Datetime'),'I am not a Datetime');

        Integer myInteger = 10;
        returnedType = RuntimeHelper.whatAmI(myInteger);
        System.assert(returnedType.equals('Integer'),'I am not a Integer');

        Decimal myDecimal = 10.56;
        returnedType = RuntimeHelper.whatAmI(myDecimal);
        System.assert(returnedType.equals('Decimal'),'I am not a Decimal');

        Double myDouble = 1261992;
        returnedType = RuntimeHelper.whatAmI(myDouble);
        System.assert(returnedType.equals('Double'),'I am not a Double');
        System.assertEquals('1.261.992', myDouble.format(),'I am not a Double');
        Contract objContract = new Contract();
        returnedType = RuntimeHelper.whatAmI(objContract.Id);
        System.assert(returnedType.equals('Id'),'I am not an Id');

        returnedType = RuntimeHelper.whatAmI(objContract);
        System.assert(returnedType.equals('Contract'),'I am not a Contract');

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .