Can Java do something like category in Objective C?
Asked Answered
W

2

18

Consider following situation:

Object > MyClass > MyClassA, MyClassB

If I want something in the Object level, for example, I added printDetail(); How can I do it in Java implementation? Moreover, can I override all the Object's method. For example, I need to have a whole new .toString(), can I override it? Thanks.

Weisburgh answered 14/3, 2012 at 4:21 Comment(0)
M
28

No it cannot, not really anyway. Objective C is a dynamically typed and scoped language, which makes it very amenable to features like categories. The closest you can come to this in Java is class instrumentation via a byte code manipulation library like ASM or Javassist.

But really, when using a strongly typed OO language like Java you should embrace its features rather than trying to duplicate those of another language.

Minard answered 14/3, 2012 at 4:29 Comment(2)
Albeit that the comment is somewhat academic, I think the issue is factoring: e.g. if I want a way to randomise the order of things in an array I might say "semantically that's most rightly an action that I'd ask array to perform; I'll therefore syntactically add it as a method on array". Which I don't think cuts against being strongly typed or object-oriented. But, no, it's not Javaish.Zippora
Isn't C# a strongly typed OO language? Because C# offers this ability through Extension Methods. What's wrong with taking good stuff from other languages? It's a way to progress...Inhumanity
A
12

As mentioned in the other answers, there is nothing really like a category. I do have some common solutions I use for some of the categories in my objective-c code when porting over to java. Many of my objective-c categories exist because I don't want to extend the iOS base classes but I do want to add some functionality to that class. Many of those objective-c categories do not add properties using objc_SetAssociatedObject. For those cases I use a static helper class in Java. Let's look at an example using NSString and String. I will add functionality to both to add quotes to the string. We'll assume this is useful and does not exist for the purposes of illustration. In objective-c we might have:

@interface NSString (MyCategory)

/**
 * Creates and autoreleased image from self.
 */
- (NSString*)quotedString;

@end

@implementation NSString (MyCategory)

- (NSString *)quotedString
{
    return [NSString stringWithFormat:@"\"%@\"", self];
}

@end

You would call this from somewhere like this:

NSString *myString = @"When you're curious, you find lots of interesting things to do.";
NSString *quotedString = [myString quotedString];

Here's how I would implement this in Java:

public class StringHelper {

    public static String quotedString(String that) {
        return '"' + that + '"';
    }
}

And to call it:

String myString = = "When you're curious, you find lots of interesting things to do.";
String quotedString = StringHelper.quotedString(myString);

If you think of the category methods as methods that automatically send the self variable as the first method argument (albeit invisible) then this makes even more sense.

For your example, if I would not extend the specific object, I might do something like:

public class ObjectHelper {

    public static void printDetail(Object that) {
        // do what it takes;
    }
}

UPDATE: A commenter asked for limitations.

Limitations would be that the code is in a separate static class. It's not as convenient as a category. You have to remember those class names, or find them, and you will not get auto completion on your original objects methods in the helper. Also, you cannot use object level properties or attributes like you get with objc_SetAssociatedObject. You could use a hash map and create something similar with the original object instance as a hash key.

public class StringHelper {
    private static Map<String, Integer> order = new HashMap();

    public static int getOrder(String that) {
        if(that == null) { return 0; }
        Integer ret = StringHelper.order.get(that);
        if(ret == null) { return 0; }
        else { return ret; }
    }
    public static void setOrder(String that, int order) {
        if(that != null) {
            StringHelper.order.put(that,  order);
        }
    }
}

There are also no name clashes with the original class, which would be more of a benefit. Name clashes in objective-c categories are considered bad.

Angadresma answered 23/1, 2014 at 19:56 Comment(3)
I like this answer best because it gives me a workaround. Thanks!Suziesuzuki
@Angadresma What is limitations if we compare this way with category in iOS?Manifesto
@Manifesto - Limitations added to an update to the answer.Angadresma

© 2022 - 2024 — McMap. All rights reserved.