28

I'd like to add a method AddDefaultNamespace() to the String class in Java so that I can type "myString".AddDefaultNamespace() instead of DEFAULTNAMESPACE + "myString", to obtain something like "MyDefaultNameSpace.myString". I don't want to add another derived class either (PrefixedString for example).

Maybe the approach is not good for you but I personally hate using +. But, anyway, is it possible to add new methods to the String class in Java?

Thanks and regards.

Talha
  • 524
  • 1
  • 6
  • 22
user15546
  • 345
  • 1
  • 3
  • 5

16 Answers16

67

String is a final class which means it cannot be extended to work on your own implementation.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
GustyWind
  • 3,026
  • 3
  • 41
  • 50
27

Well, actually everyone is being unimaginative. I needed to write my own version of startsWith method because I needed one that was case insensitive.

class MyString{
    public String str;
    public MyString(String str){
        this.str = str;
    }
    // Your methods.
}

Then it's quite simple, you make your String as such:

MyString StringOne = new MyString("Stringy stuff");

and when you need to call a method in the String library, simple do so like this:

StringOne.str.equals("");

or something similar, and there you have it...extending of the String class.

Aldo Barreras
  • 271
  • 3
  • 2
19

As everyone else has noted, you are not allowed to extend String (due to final). However, if you are feeling really wild, you can modify String itself, place it in a jar, and prepend the bootclasspath with -Xbootclasspath/p:myString.jar to actually replace the built-in String class.

For reasons I won't go into, I've actually done this before. You might be interested to know that even though you can replace the class, the intrinsic importance of String in every facet of Java means that it is use throughout the startup of the JVM and some changes will simply break the JVM. Adding new methods or constructors seems to be no problem. Adding new fields is very dicey - in particular adding Objects or arrays seems to break things although adding primitive fields seems to work.

Alex Miller
  • 69,183
  • 25
  • 122
  • 167
  • i’ll try this. string needs to be iterable. and while we are at it: how come java is so damn awful? no sane person would specify `remove` in an interface, just to add `throws UnsupportedOperationException - if the remove operation is not supported by this Iterator`. – flying sheep Feb 15 '11 at 00:42
  • Nice experiment but since it's only safe to add new constructors or methods there doesn't seem to be any valid reason to take this approach over what @aldo-barreras suggested – Ozzy Jul 09 '13 at 21:09
6

It is not possible, since String is a final class in Java.

You could use a helper method all the time you want to prefix something. If you don't like that you could look into Groovy or Scala, JRuby or JPython both are languages for the JVM compatible with Java and which allow such extensions.

jrudolph
  • 8,307
  • 4
  • 32
  • 50
5

YES!

Based on your requirements (add a different namespace to a String and not use a derived class) you could use project Lombok to do just that and use functionality on a String like so:

String i = "This is my String";
i.numberOfCapitalCharacters(); // = 2

Using Gradle and IntelliJ idea follow the steps below:

  1. Download the lombok plugin from intelliJ plugins repository.
  2. add lombok to dependencies in gradle like so: compileOnly 'org.projectlombok:lombok:1.16.20'
  3. go to "Settings > Build > Compiler > Annotation Processors" and enable annotation processing
  4. create a class with your extension functions and add a static method like this:

    public class Extension {
        public static String appendSize(String i){
            return i + " " + i.length();
        }
    }
    
  5. annotate the class where you want to use your method like this:

    import lombok.experimental.ExtensionMethod;
    
    @ExtensionMethod({Extension.class})
    public class Main {
        public static void main(String[] args) {
            String i = "This is a String!";
            System.out.println(i.appendSize());
        }
    }
    

Now you can use the method .appendSize() on any string in any class as long as you have annotated it and the produced result for the above example

This is a String!

would be:

This is a String! 17

Tom
  • 16,842
  • 17
  • 45
  • 54
PaulB
  • 1,554
  • 2
  • 16
  • 34
  • IntelliJ is highlighting my extension method as if it is an error and then it compiles fine. – Noah Wilder Oct 26 '18 at 23:06
  • This is not a class extension, only bootstrap method can replace String class. However it simulates method addition very well, recompiling Main to call static methods on Extension. Is the perfect answer for this question. But take care, if you call String.class.getDeclaredMethod, it will not be found. – Dyorgio Dec 13 '20 at 17:47
3

The class declaration says it all pretty much,as you cannot inherit it becouse it's final. You can ofcourse implement your own string-class, but that is probaby just a hassle.

public final class String

C# (.net 3.5) have the functionality to use extender metods but sadly java does not. There is some java extension called nice http://nice.sourceforge.net/ though that seems to add the same functionality to java.

Here is how you would write your example in the Nice language (an extension of Java):

private String someMethod(String s)
{
   return s.substring(0,1);

}

void main(String[] args)
{
   String s1 = "hello";
   String s2 = s1.someMethod();
   System.out.println(s2);

}

You can find more about Nice at http://nice.sf.net

Carl-Johan
  • 103
  • 1
  • 7
2

People searching with keywords "add method to built in class" might end up here. If you're looking to add method to a non final class such as HashMap, you can do something like this.

public class ObjectMap extends HashMap<String, Object> {

    public Map<String, Object> map;

    public ObjectMap(Map<String, Object> map){
        this.map = map;
    }

    public int getInt(String K) {
        return Integer.valueOf(map.get(K).toString());
    }

    public String getString(String K) {
        return String.valueOf(map.get(K));
    }

    public boolean getBoolean(String K) {
        return Boolean.valueOf(map.get(K).toString());
    }

    @SuppressWarnings("unchecked")
    public List<String> getListOfStrings(String K) {
        return (List<String>) map.get(K);
    }

    @SuppressWarnings("unchecked")
    public List<Integer> getListOfIntegers(String K) {
        return (List<Integer>) map.get(K);
    }

    @SuppressWarnings("unchecked")
    public List<Map<String, String>> getListOfMapString(String K) {
        return (List<Map<String, String>>) map.get(K);
    }

    @SuppressWarnings("unchecked")
    public List<Map<String, Object>> getListOfMapObject(String K) {
        return (List<Map<String, Object>>) map.get(K);
    }

    @SuppressWarnings("unchecked")
    public Map<String, Object> getMapOfObjects(String K) {
        return (Map<String, Object>) map.get(K);
    }

    @SuppressWarnings("unchecked")
    public Map<String, String> getMapOfStrings(String K) {
        return (Map<String, String>) map.get(K);
    }
}

Now define a new Instance of this class as:

ObjectMap objectMap = new ObjectMap(new HashMap<String, Object>();

Now you can access all the method of the built-in Map class, and also the newly implemented methods.

objectMap.getInt("KEY");

EDIT:

In the above code, for accessing the built-in methods of map class, you'd have to use

objectMap.map.get("KEY");

Here's an even better solution:

public class ObjectMap extends HashMap<String, Object> {

    public ObjectMap() {

    }

    public ObjectMap(Map<String, Object> map){
        this.putAll(map);
    }

    public int getInt(String K) {
        return Integer.valueOf(this.get(K).toString());
    }

    public String getString(String K) {
        return String.valueOf(this.get(K));
    }

    public boolean getBoolean(String K) {
        return Boolean.valueOf(this.get(K).toString());
    }

    @SuppressWarnings("unchecked")
    public List<String> getListOfStrings(String K) {
        return (List<String>) this.get(K);
    }

    @SuppressWarnings("unchecked")
    public List<Integer> getListOfIntegers(String K) {
        return (List<Integer>) this.get(K);
    }

    @SuppressWarnings("unchecked")
    public List<Map<String, String>> getListOfMapString(String K) {
        return (List<Map<String, String>>) this.get(K);
    }

    @SuppressWarnings("unchecked")
    public List<Map<String, Object>> getListOfMapObject(String K) {
        return (List<Map<String, Object>>) this.get(K);
    }

    @SuppressWarnings("unchecked")
    public Map<String, Object> getMapOfObjects(String K) {
        return (Map<String, Object>) this.get(K);
    }

    @SuppressWarnings("unchecked")
    public Map<String, String> getMapOfStrings(String K) {
        return (Map<String, String>) this.get(K);
    }

    @SuppressWarnings("unchecked")
    public boolean getBooleanForInt(String K) {
        return Integer.valueOf(this.get(K).toString()) == 1 ? true : false;
    }
}

Now you don't have to call

objectMap.map.get("KEY");

simply call

objectMap.get("KEY");
Drunken Daddy
  • 7,326
  • 14
  • 70
  • 104
2

Not possible, and that's a good thing. A String is a String. It's behaviour is defined, deviating from it would be evil. Also, it's marked final, meaning you couldn't subclass it even if you wanted to.

Sietse
  • 7,884
  • 12
  • 51
  • 65
2

As everybody else has said, no you can't subclass String because it's final. But might something like the following help?

public final class NamespaceUtil {

    // private constructor cos this class only has a static method.
    private NamespaceUtil() {}

    public static String getDefaultNamespacedString(
            final String afterDotString) {
        return DEFAULT_NAMESPACE + "." + afterDotString;
    }

}

or maybe:

public final class NamespacedStringFactory {

    private final String namespace;

    public NamespacedStringFactory(final String namespace) {
        this.namespace = namespace;
    }

    public String getNamespacedString(final String afterDotString) {
        return namespace + "." + afterDotString;
    }

}
MB.
  • 7,365
  • 6
  • 42
  • 42
1

No You Cannot Modify String Class in java. Because It's final class. and every method present in final class by default will be final.

The absolutely most important reason that String is immutable or final is that it is used by the class loading mechanism, and thus have profound and fundamental security aspects.

Had String been mutable or not final, a request to load "java.io.Writer" could have been changed to load "mil.vogoon.DiskErasingWriter"

Vpn_talent
  • 1,290
  • 12
  • 21
1

Better use StringBuilder, which has method append() and does the job you want. The String class is final and can not be extended.

m_pGladiator
  • 8,462
  • 7
  • 43
  • 61
0

All is said by the other contributors before. You can not extend String directly because it is final.

If you would use Scala, you can use implicit conversions like this:

object Snippet {
  class MyString(s:String) {
    def addDefaultNamespace = println("AddDefaultNamespace called")
  }
  implicit def wrapIt(s:String) = new MyString(s)

  /** test driver */
  def main(args:Array[String]):Unit = {
    "any java.io.String".addDefaultNamespace // !!! THAT is IT! OR?
  }
0

You can do this easily with Kotlin. You can run both the kotlin code from within the java and the java code from the kotlin.

Difficult jobs that you can do with Java can be done more easily with Kotlin. I recommend every java developer to learn kotlin.

Referance: https://kotlinlang.org/docs/java-to-kotlin-interop.html

Example:

Kotlin StringUtil.kt File

@file:JvmName("StringUtil")
package com.example

fun main() {
val x: String = "xxx"
println(x.customMethod())
}

fun String.customMethod(): String = this + " ZZZZ"

Java Code:

package com.example;

public class AppStringCustomMethod {

public static void main(String[] args) {
    String kotlinResponse = StringUtil.customMethod("ffff");
    System.out.println(kotlinResponse);
}
}

output:

ffff ZZZZ

Ogün
  • 97
  • 2
  • 10
0

The Java String class is a final, making it immutable. This is for efficiency reasons and that it would be extremely difficult to logically extend without error; the implementers have therefore chosen to make it a final class meaning it cannot be extended with inheritance.

The functionality you wish your class to support is not properly part of the regular responsibilities of a String as per the single responsibility principle, a namespace it is a different abstraction, it is more specialised. You should therefore define a new class, which includes String a member and supports the methods you need to provide the namespace management you require.

Do not be afraid to add abstractions (classes) these are the essence of good OO design.

Try using a class responsibility collaboration (CRC) card to clarify the abstraction you need.

Martin Spamer
  • 5,437
  • 28
  • 44
-2

You can create your own version of String class and add a method :-)

-2

Actually , you can modify the String class . If you edit the String.java file located in src.zip , and then rebuild the rt.jar , the String class will have more methods added by you . The downside is that that code will only work on your computer , or if you provide your String.class , and place it in the classpath before the default one .

Vhaerun
  • 12,806
  • 16
  • 39
  • 38