Related
While creating JUnit test cases, it takes a long time to reconstruct objects for every single one of them and perform some operations that all my unit tests use.
Is there anyway I can make some objects in a test case that I can freely use in all of my tests without recreating them each time?
Thanks in advance!
A simple way of doing this is to create a private method that creates test objects. These can take in the parameters (the ones that need to change in the various test cases), or just provide a default object that you could in turn change. If the same objects are used in multiple tests, then a testdata-builder might be what you are looking for.
Say you have a class like this:
public class Something {
private String someString;
private Integer someInt;
public Something(final String someString, final Integer someInt) {
this.someString = someString;
this.someInt = someInt;
}
//getters and stuff
}
Then you can create a testdata builder like this:
public class SomethingBuilder {
private String someString;
private Integer someInt;
public SomethingBuilder() {
someString = "Some default value";
someInt = 42;
}
public SomethingBuilder withSomeString(final String someString) {
this.someString = someString;
return this;
}
public SomethingBuilder withSomeInt(final Integer someInt) {
this.someInt = someInt;
return this;
}
public Something build() {
final Something something = new Something(someString, someInt);
return something;
}
}
Then, creating test data becomes really simple, you can mutate the fields you different than your default values easily:
final Something something =
new SomethingBuilder().withSomeString("I want to override the default!").build();
Might seem like a bit of overkill for my small, example class, but if you have a central data class that appears in many tests, it will save you a lot of time and lines of code.
In my Android application I have a class which gives me static string values; something like this:
public class VehicleInfo {
public static String getVehicleEnginePower(boolean isNew) {
return isNew ? "1800CC" : "1600CC";
}
}
Now I have another category, so I will have to pass another Boolean, and I will get the value I need. However, these categories will keep on increasing. So I looked into the Open/Closed principle which looks promising for quick enhancement. To ensure this I will make the VehicleInfo class as an Interface and then I will have other classes implement VehicleInfo.
public interface VehicleInfo {
String getVehicleEnginePower();
}
public class NewVehicle implements VehicleInfo {
#Override
public String getVehicleEnginePower() {
return "1800CC";
}
}
and the other category classes will also be something like this. In this way I will have to add another class for all the new categories.
The question I wanted to ask is: is there a way that I can have single instance of this interface? Because in the whole application flow, a user will only be able to see one category until he switches to another category.
I don't want to instantiate these classes at multiple points. To clarify my question, I want to do something like this at the start of my application:
if (isNew) {
VehicleInfo vehicleInfor = new NewVehicle();
}
And in the whole application, whenever I call VehicleInfo.getVehicleEnginePower, it should always return engine power from the NewVehicle class.
Is something like this possible? Or am I just being silly and I will have to instantiate this interface on multiple points?
Maybe you need a singleton here
public class VehicleInfoManager {
private static VehicleInfoManager INSTANCE = new VehicleInfoManager();
private VehicleInfo currentVehicleInfo;
public static VehicleInfoManager getInstance() {
return INSTANCE;
}
public void setCurrentVehicleInfo(VehicleInfo info) {
this.currentVehicleInfo = info;
}
public String getVehicleEnginePower() {
return this.currentVehicleInfo.getVehicleEnginePower();
}
private VehicleInfoManager() {
// Constructor private by default
}
}
Then you can call it from everywhere like this
VehicleInfoManager.getInstance().getVehicleEnginePower()
//Or set current info like this
VehicleInfoManager.getInstance().setCurrentVehicleInfo(new NewVehicle())
Just be careful as currentVehicleInfo is null by default so you need to handle null pointer cases.
If I understand your question correctly.
My solution to this would be Enum
public enum VehicleEnginePower {
NEW ("1800CC"),
OLD ("1600CC"),
private final String name;
private Modes(String s) {
name = s;
}
public String toString() {
return this.name;
}
}
Then you can do
if (isNew) {
String powerOfEngine = VehicleEnginePower.NEW.toString();
}
First of all, sorry for the bad title. I don't know how to describe the problem in a few words (maybe not even in many)...
I am refactoring some settings in our system to be more abstract. The current solution has multiple tables in the DB, one for each settings area. In order to add a new setting, you'll need to extend the schema, the hibernate class, all transfer object classes, getters/setters, etc. I felt that this is violating OCP (open-closed principle), thus the refactoring.
I've spent some time coming up with ideas on how to implement such an abstraction. My favourite idea so far is the following:
1 enum for each settings area
1 enum value for each setting
Each setting is a SettingsDefinition<T> class using a generic type
A SettingsService is using static get/set methods with generic types
So for example, a settings area could be:
public enum SettingsABC{
A(new SettingDefinition<Integer>("A", 123)),
B(new SettingDefinition<String>("B", "Hello")),
C(new SettingDefinition<Boolean>("C", false));
private SettingDefinition settingDefinition;
SettingsABC(SettingDefinition settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition getDefinition() {
return settingDefinition;
}
}
Where the SettingDefinition is the following:
public class SettingDefinition<T> {
private String name;
private T defaultValue;
public SettingDefinition(String name, T defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
}
public String getName() {
return name;
}
public T getDefaultValue() {
return defaultValue;
}
}
And the service to get/set the values would be:
public class SettingsService {
public static <T> T getSetting(SettingDefinition setting) {
// hit db to read
// return value
}
public static <T> void setSetting(SettingDefinition setting, T value) {
// hit db to write
}
}
And the consumer would look something like this:
String value = SettingsService.getSetting(SettingsABC.B.getDefinition());
SettingsService.setSetting(SettingsABC.A.getDefinition(), 123);
My problem is that I cannot enforce a compiler type check between the generic type of the SettingDefinition inside SettingsABC and the generic type of get/set methods of the service. So in essence, I can do this:
Integer value = SettingsService.getSetting(SettingsABC.B.getDefinition());
Where B's definition is of type String.
Also, I can do this:
SettingsService.setSetting(SettingsABC.A.getDefinition(), "A");
Where A's definition is an Integer.
Is there any way to use generics to force these two different generic types match?
You can convert the enum to the class:
public final class SettingsABC<T> {
public static final SettingsABC<Integer> A =
new SettingsABC<>(new SettingDefinition<>("A", 123));
public static final SettingsABC<String> B =
new SettingsABC<>(new SettingDefinition<>("B", "Hello"));
public static final SettingsABC<Boolean> C =
new SettingsABC<>(new SettingDefinition<>("C", false));
private final SettingDefinition<T> settingDefinition;
// private constructor, so nobody else would instantiate it
private SettingsABC(SettingDefinition<T> settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition<T> getDefinition() {
return settingDefinition;
}
}
This way individual constants will be typed. Now you can use the type arguments for SettingService as well:
public static <T> T getSetting(SettingDefinition<T> setting) {
...
}
public static <T> void setSetting(SettingDefinition<T> setting, T value) {
...
}
Although it's not an enum anymore, it can be used mostly in the same way. If you need other methods which are usually available in enum, you can mimic them like this:
public String name() {
return settingDefinition.getName();
}
#Override
public String toString() {
return settingDefinition.getName();
}
// and so on
I have;
public enum Detailed {
PASSED, INPROCESS, ERROR1, ERROR2, ERROR3;
}
and need to convert it to the following;
public enum Simple {
DONE, RUNNING, ERROR;
}
So first PASSED->DONE and INPROCESS->RUNNING, but all errors should be: ERROR. Obviously it is possible to write cases for all values, but there may be a better solution?
Personally I would just create a Map<Detailed, Simple> and do it explicitly - or even use a switch statement, potentially.
Another alternative would be to pass the mapping into the constructor - you could only do it one way round, of course:
public enum Detailed {
PASSED(Simple.DONE),
INPROCESS(Simple.RUNNING),
ERROR1(Simple.ERROR),
ERROR2(Simple.ERROR),
ERROR3(Simple.ERROR);
private final Simple simple;
private Detailed(Simple simple) {
this.simple = simple;
}
public Simple toSimple() {
return simple;
}
}
(I find this simpler than Ted's approach of using polymorphism, as we're not really trying to provide different behaviour - just a different simple mapping.)
While you could potentially do something cunning with the ordinal value, it would be much less obvious, and take more code - I don't think there'd be any benefit.
One way is to define a method asSimple() in your Detailed enum:
public enum Detailed {
PASSED {
#Override
Simple asSimple() {
return DONE;
}
},
INPROCESS {
#Override
Simple asSimple() {
return RUNNING;
}
},
ERROR1,
ERROR2,
ERROR3;
public Simple asSimple() {
return Simple.ERROR; // default mapping
}
}
You can then simply call the method when you want to do the mapping:
Detailed code = . . .
Simple simpleCode = code.asSimple();
It has the advantage of putting the knowledge of the mapping with the Detailed enum (where perhaps it belongs). It has the disadvantage of having knowledge of Simple mixed in with the code for Detailed. This may or may not be a bad thing, depending on your system architecture.
Use EnumMap
I decouple my external xml interface from my internal domain model by implementing a transformation service. This includes mapping enums from jaxb generated code to domain model enums.
Using a static EnumMap encapsulates the concern of transformation within the class responsible for transformation. Its cohesive.
#Service
public class XmlTransformer {
private static final Map<demo.xml.Sense, Constraint.Sense> xmlSenseToSense;
static {
xmlSenseToSense = new EnumMap<demo.xml.Sense, Constraint.Sense> (
demo.xml.Sense.class);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.EQUALS,
Constraint.Sense.EQUALS);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.GREATER_THAN_OR_EQUALS,
Constraint.Sense.GREATER_THAN_OR_EQUALS);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.LESS_THAN_OR_EQUALS,
Constraint.Sense.LESS_THAN_OR_EQUALS);
}
...
}
Guava's Enums.getIfPresent() on Enum.name()
Our case was a particular specialization of this one. We do have two Enum: one we use in the application and another one we use in the core library. The core library is used by a handful of applications, by different teams. Each application views a subset of the whole functionality. The whole functionality is configured with the enums in order to switch on and off, throttle up or down, select strategies, etc.
So we ended up with:
one enum for the library, containing all the possible configurations, visible from the applications and also some library-specific
one enum for each application, containing the literals corresponding to what the application can see/touch in the library, and some application-specific
Then as we pass data down to the library, we adapt all data and also those configurations. We own all enums, so we can choose to call the same configuration with the same literal in different enums.
Enum LibraryConfig {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_THREE,
FUNCTION_FOUR;
}
Enum Aplication1Config {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_THREE,
APPL1_FUNCTION_ONE,
APPL1_FUNCTION_TWO;
}
Enum Aplication2Config {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_FOUR;
APPL2_FUNCTION_ONE;
}
When we need to convert from one type to another (app --> lib or lib --> app) we use the getIfPresent() method from com.google.common.base.Enums in this way:
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = Enums.getIfPresent(LibraryConfig.class, config1App1.name()).orNull();
We check configLib for null value to see if there was successful conversion. This last step we use because of the APPX_FUNCTION_YYY, which are application-specific, and for the conversion on the direction lib --> app, not to pass configuration values library-specific (FUNCTION_FOUR in the example).
maven's dependency management:
Just in case anyone needs it:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
Home grown version:
You can make your own conversion using the Enum methods, but you have to take care of the exception to detect when the conversion did not succeed:
try {
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = LibraryConfig.valueOf(config1App1.name());
} catch (IllegalArgumentException iae) {
// if the conversion did not succeed
}
Ted's answer is very Javaly, but the expression
passed == PASSED ? DONE : ERROR
would do the job, too.
To me that sounds more like a conceptual problem than a programming problem. Why don't you just remove the "Simple" enum type and use the other one instead in all places in the program?
Just to make that more clear with another example: Would you really try to define an enum type for the work days in a week (Monday to Friday) and another enum for all days of a week (Monday to Sunday)?
Here is the simple enum mapper with test:
-- IMPLEMENTATION
-- ENUMS
public enum FirstEnum {
A(0), B(1);
private final int value;
private FirstEnum(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public enum SecondEnum {
C(0), D(1);
private final int valueId;
private SecondEnum(int valueId) {
this.valueId = valueId;
}
public int getValueId() {
return valueId;
}
}
--MAPPER
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.Validate;
import com.google.common.collect.Sets;
public class EnumPropertyMapping {
private final Map<?, ?> firstMap;
private final Map<?, ?> secondMap;
private final Class<?> firstType;
private final Class<?> secondType;
private EnumPropertyMapping(
Map<?, ?> firstMap, Map<?, ?> secondMap, Class<?> firstType, Class<?> secondType) {
this.firstMap = firstMap;
this.secondMap = secondMap;
this.firstType = firstType;
this.secondType = secondType;
}
public static Builder builder() {
return new Builder();
}
#SuppressWarnings("unchecked")
public <R> R getCorrespondingEnum(Object mappedEnum) {
Validate.notNull(mappedEnum, "Enum must not be NULL");
Validate.isInstanceOf(Enum.class, mappedEnum, "Parameter must be an Enum");
if (firstType.equals(mappedEnum.getClass())) {
return (R) firstMap.get(mappedEnum);
}
if (secondType.equals(mappedEnum.getClass())) {
return (R) secondMap.get(mappedEnum);
}
throw new IllegalArgumentException("Didn't found mapping for enum value: " + mappedEnum);
}
public static class Builder {
private final Map<Object, Object> firstEnumMap = new HashMap<>();
private final Map<Object, Object> secondEnumMap = new HashMap<>();
private Class<?> firstEnumType;
private Class<?> secondEnumType;
public <T extends Enum<T>> Builder addFirst(Class<T> enumType, String propertyName) {
firstEnumType = enumType;
initMap(firstEnumMap, enumType.getEnumConstants(), propertyName);
return this;
}
public <T extends Enum<T>> Builder addSecond(Class<T> enumType, String propertyName) {
secondEnumType = enumType;
initMap(secondEnumMap, enumType.getEnumConstants(), propertyName);
return this;
}
private void initMap(Map<Object, Object> enumMap, Object[] enumConstants, String propertyName) {
try {
for (Object constant : enumConstants) {
enumMap.put(PropertyUtils.getProperty(constant, propertyName), constant);
}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
}
public EnumPropertyMapping mapEnums() {
Validate.isTrue(firstEnumMap.size() == secondEnumMap.size());
Validate.isTrue(Sets.difference(firstEnumMap.keySet(), secondEnumMap.keySet()).isEmpty());
Map<Object, Object> mapA = new HashMap<>();
Map<Object, Object> mapB = new HashMap<>();
for (Map.Entry<Object, Object> obj : firstEnumMap.entrySet()) {
Object secondMapVal = secondEnumMap.get(obj.getKey());
mapA.put(obj.getValue(), secondMapVal);
mapB.put(secondMapVal, obj.getValue());
}
return new EnumPropertyMapping(mapA, mapB, firstEnumType, secondEnumType);
}
}
}
-- TEST
import org.junit.Test;
import com.bondarenko.common.utils.lang.enums.FirstEnum;
import com.bondarenko.common.utils.lang.enums.SecondEnum;
import static junit.framework.TestCase.assertEquals;
public class EnumPropertyMappingTest {
#Test
public void testGetMappedEnum() {
EnumPropertyMapping mapping = EnumPropertyMapping.builder()
.addSecond(SecondEnum.class, "valueId")
.addFirst(FirstEnum.class, "value")
.mapEnums();
assertEquals(SecondEnum.D, mapping.getCorrespondingEnum(FirstEnum.B));
assertEquals(FirstEnum.A, mapping.getCorrespondingEnum(SecondEnum.C));
}
}
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.
String is a final class which means it cannot be extended to work on your own implementation.
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.
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.
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.
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:
Download the lombok plugin from intelliJ plugins repository.
add lombok to dependencies in gradle like so: compileOnly 'org.projectlombok:lombok:1.16.20'
go to "Settings > Build > Compiler > Annotation Processors" and enable annotation processing
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();
}
}
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
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
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.
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;
}
}
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");
Better use StringBuilder, which has method append() and does the job you want. The String class is final and can not be extended.
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"
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?
}
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.
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
You can create your own version of String class and add a method :-)
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 .