There is a Message superclass and there are various Message subclasses like WeddingMessage, GreetingMessage, FarewellMessage, Birthday Message.
The Message superclass has a constructor:
public Message(String messageType){
this.messageType = messageType;
}
The message subclasses all have different constructors, but they all make a call to the superclass, where they pass the messageType as an argument So for example:
public BirthdayMessage( String name, int age){
super("birthday");
System.out.println("Happy birthday " + name + "You are " + age " years old");
public FareWellMessage(String name, String message){
super("farewell");
System.out.println(message + " " + name);
}
The messageType which is created is determined by arguments passed in by the user. So for example, if a user inserts 'birthday John 12', then a BirthdayMessage will be created with parameters John and 12. If a user enters 'farewell Grace take care' then an instance of FarewellMessage is created with those parameters.
Instead of having a bunch of if/else statements or a switch case, in the form of something like-
words[] = userinput.slice(' ');
word1 = words[0];
if (word1 == birthday)
create new BirthdayMessage(parameters here)
if (word1 == wedding)
create new weddingMessage(parameters here)
etc
How could i use reflection to determine which type of Message class to create.
My current idea is to use the File class to get all the Files in the package which contain the message subclasses. Then use reflection to get each of their constructor parameter types and see if they match the parameters given by user input. Then make instances of those matching classes with random parameters. When made, the subclass will make a call to its superclass constructor with its messageType. Then i can check to see if the messageType variable matches the user input.
So if the user enters 'birthday john 23'
I find all constructors in the package that take a String and an int as parameters and that have a field messageType(inherited from Message). Then i create an instance of that class and check if the messageType is == to the first word in the user input (birthday in this case). If it is, then i create an instance of that class with the user provided parameters.
Is there a better way to do this with reflection?
If you want to go this route (I hate reflection, but it has it's uses) make sure you isolate it inside a factory class. I would recommend looking into #Annotations and mark the classes you want to be scanned with a specific annotation.
Something like: (must admit, actually had fun writing this example)
annotation:
#Target(value = {ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface GreetingcardInstance {
public String value();
}
Your 2 message classes and base class
public abstract class Message {
private String name;
public Message(String name) {
this.name = name; // not used, but to keep it in line with your example
}
}
#GreetingcardInstance("birthday")
public class BirthdayMessage extends Message {
public BirthdayMessage(Integer i) {
super("birthday");
// this line prints if this works.
System.out.println("Birthdaymessage created: " +i);
}
}
#GreetingcardInstance("other")
public class OtherMessage extends Message{
public OtherMessage(Integer i, Integer j) {
super("other");
}
}
And the factory that hides the nasty reflection code
public class CardFactory {
private final Map<String, Class> messageClasses;
public CardFactory() {
// I had all my implementations of Message in the package instances
Reflections reflections = new Reflections("instances");
Set<Class<?>> greetingCardAnnotations = reflections.getTypesAnnotatedWith(GreetingcardInstance.class);
Map<String, Class> result = new HashMap<String, Class>();
for (Class c : greetingCardAnnotations) {
if (Message.class.isAssignableFrom(c)) {
GreetingcardInstance annotation = (GreetingcardInstance) c.getAnnotation(GreetingcardInstance.class);
result.put(annotation.value(), c);
}
}
messageClasses = result;
}
public Message createMessage(String messageType, Object[] arguments) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = messageClasses.get(messageType);
if (clazz == null) {
throw new IllegalArgumentException("MessageType: " + messageType + " not supported");
}
Class[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; ++i) {
argumentTypes[i] = arguments[i].getClass();
}
Constructor constructor = clazz.getConstructor(argumentTypes);
return (Message) constructor.newInstance(arguments);
}
}
You can either use spring or google's library or scan them by hand, though you'll find that's a pain in the behind. In this example I used the google library which works pretty well.
in this particular implementation the classes all live in the same package. I don't think this is too bad but might not work for you.
I've also not handled basic types, the constructors in this case take Integer, not int as I originally intended.
When you parse a string, just parse the arguments into String, INteger, etc. and pass them as an Object[] , whcih will be used as constructor args.
public static void main(String[] argv) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
CardFactory cf = new CardFactory();
System.out.println(cf.toString());
cf.createMessage("birthday", new Object[] { new Integer(0) });
}
output:
Birthdaymessage created: 0
Wouldn't it be simpler to instead of creating different classes for each message type use different formats which you could store somewhere like Map<String,String>?
I mean something like
Map<String,String> formats = new HashMap<>();
formats.put("birthday","Happy birthday %s. You are %d years old%n");//name, age
formats.put("farewell","%s %s%n");//message, name
Object[] data = {"Dany", 5};
System.out.printf(formats.get("birthday"),data);
data = new Object[]{"Ferwell Jack.","We will miss you"};
System.out.printf(formats.get("farewell"),data);
If you don't want to recompile your code after each change in formats you can store them in file and load when application starts, or when needed.
Simple way to do it is by java.util.Properties class.
You can create formats.properties file with content like
birthday=Happy birthday %s. You are %d years old%n
farewell=%s %s%n
and code using it could look like
Properties formatProp = new Properties();
formatProp.load(new FileReader("formats.properties"));//
Object[] data = {"Dany", 5};
System.out.printf(formatProp.getProperty("birthday"),data);
data = new Object[]{"Ferwell Jack.","We will miss you"};
System.out.printf(formatProp.getProperty("farewell"),data);
There are lots of ways to do what you want. One way would be to learn how to use an injection library like Google Guice. You'll probably get the most mileage out of that in the long run. Another option would be to learn a language like Clojure edit Clojure example added at end.
If you'd like to see a minimal example of what would look like Java, the following class' main will show you how to do that. Basically, it takes a map of String->Classnames (strings), and turns it into a map of String->Class (objects), then a super simple builder method looks up the codeword in the map and constructs a new instance that class and returns it.
The main builds two of them and prints their output. e.g.
I am a bar.
I'm a baz!
Here's the Java program. If you change the package, you'll have to change the classnames in the textConfig variable. The equivalent Clojure code follows.
package foo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public abstract class Foo {
public abstract String something(); // the abstract method
static class Bar extends Foo { // one subclass
#Override public String something() {
return "I am a bar.";
}
}
static class Baz extends Foo { // another subclass
#Override public String something() {
return "I'm a baz!";
}
}
public static Class loadClass(String classname) {
try { // wrapper for Class.forName that doesn't throw checked exception
return Class.forName(classname);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(ex);
}
}
public static Map<String, Class> buildConfig(Map<String, String> textConfig) {
// turn {codeword, classname} into {codeword, class} entries
// java 8 voodoo follows...
return textConfig.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
e -> loadClass(e.getValue())));
}
public static Foo construct(Map<String, Class> config, String codeword) {
try { // lookup codeword and return new instance of class
return (Foo)config.get(codeword).newInstance();
}
catch(InstantiationException | IllegalAccessException ex) {
throw new IllegalArgumentException(ex);
}
}
public static void main(String[] args) {
// some configuration, you could hardcode this, or even put the
// data in annoations if you want to be fancy
Map<String, String> textConfig = new HashMap<>();
textConfig.put("codeword-bar", "foo.Foo$Bar");
textConfig.put("codeword-baz", "foo.Foo$Baz");
// turn your text config into something more immediately useful
Map<String, Class> config = buildConfig(textConfig);
// show that it works.
System.out.println(construct(config, "codeword-bar").something());
System.out.println(construct(config, "codeword-baz").something());
}
}
edit
The verbosity of the above code was driving me nuts. So, in case you're interested, here's equivalent code in Clojure.
It puts two functions into a map with the keys :bar and :baz and it looks them up, invokes them and prints the return value.
user=> (def config {:bar (fn [] "I am a bar.") :baz (fn [] "I'm a bar!")})
#'user/config
user=> (println ((:bar config)))
I am a bar.
nil
user=> (println ((:baz config)))
I'm a bar!
nil
Related
Since I'm a newbie, I would like to know if there is a better way to code this.
Let say we have batch (spring) where we have downloader/processor/mapper/writer for every type of file we receive since we have customized logic for each file type. X number of Mapper , X number of processor for X number of file types.
Currently looking into templatize the code so not much changes may be required when new type is introduced. Below is my idea. so let say mapper, we have different objects for different file types and all of them will be converted to object of Class CustomObject as below. mapper bean in sample spring context
bean id = "file1Mapper" class = "com.filemapper.file1Mapper"
and it invokes file1Mapper class which has mapping logic. Same for other files.
This is what I'm coming up with to avoid all those file1mapper, file2mapper...... instead one generic mapper which does all together, but looking for better solutions,
public class GMapper{
public <T> CustomObject map(T item){
CustomObject customObject = new CustomObject()
.WithABCDetails(getABCDetails(item));
}
private <T> XYZDetails getABCDetails(T item) {
ABCDetails details = new ABCDetails();
if( item instanceof A){
A a = (A)item;
// read a and map it to ABCDetails object
}
if( item instanceof B){
B b = (B)item;
// read b and map it to ABCDetails object
}
...
...
// repeat this if loop for mapping all file types.
return details;
}
}
Sample jsons
class ABCDetails{
// JsonProperty
Object1 ob1;
Object2 ob2;
Integer d;
}
class Object1{
// JsonProperty
Object3 ob3;
String abc;
String def;
}
class Object2{
// JsonProperty
String ab;
Integer e;
}
class A{
// JsonProperty
String e;
String d; // ex, this is mapped to Object 2 String "ab"
}
This does't look so professional and I believe there might be better ways to do it. Can someone please share an example or explanation on how can this code be made better. I also reading Functional interface to see if that could help.
Thanks in advance.
It is impossible to understand what you need. So I will give some common advice.
Format your code - use tabs/spaces to indent.
Do not put capital letters together - replace ABCDetails with AbcDetails. No one cares how real world name looks like.
Do not write meaningless comments - say no to // JsonProperty
Name variables so that someone can understand what they are supposed to store - avoid {Object1 ob1; Object2 ob2; Integer d;}
Do not write if ... else if ... else if ... or case when ... since this scales badly. Use Map. Examples below.
And a general solution to your problem: use plugin architecture - the best thing (and maybe the only thing) that OOP can offer. Just make all your processors implement common interface. And to work with plugins use dispatcher pattern.
First create all processors.
public interface FileProcessor {
String extension();
void process(String filename);
}
#Component
public final class CsvFileProcessor implements FileProcessor {
public String extension() {
return "csv";
}
public void process(String filename) {
/* do what you need with csv */
}
}
#Component
public final class JsonFileProcessor implements FileProcessor {
public String extension() {
return "json";
}
public void process(String filename) {
/* do what you need with json */
}
}
Then inject them into your dispatcher. Do not forget to process errors, for example, some files may not have suffix, for some files you will not have processor, etc.
#Component
public final class FileDispatcher {
private final Map<String, FileProcessor> processorByExtension;
#Autowired
public FileDispatcher(List<FileProcessor> processors) {
processorByExtension = processors.stream().collect(Collectors.toMap(p -> p.extension(), p -> p));
}
public void dispatch(String filename) {
String extension = filename.split("//.")[1];
processorByExtension.get(extension).process(filename);
}
}
Now if you need to support new file format you have to add only one class - implementation of FileProcessor. You do not have to change any of already created classes.
In my program, different features are divided into different modules, potentially hundreds of modules,
each module is a subclass of an abstract Module class
which look something like this
public abstract class Module {
public final String name;
public Module(String name){
this.name = name;
}
public abstract void execute();
}
with subclasses that look like this
public class Mod1 extends Module{
public Mod1() {
super("Mod1");
}
#Override
public void execute() {
//Do something
}
}
and I need to make a list of instances of all modules but doing it like this is kinda tedious since my program might have hundreds of modules and it might be hard to debug(I might miss a few lol)
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
MODULES.add(new Mod1());
MODULES.add(new Mod2());
MODULES.add(new Mod3());
MODULES.add(new Mod4());
MODULES.add(new Mod5());
}
so I think using reflection might be the way to go but after a quick google search, I see many people don't like reflection in production code, so I come here to ask if this is an appropriate case to use reflection or is there any reason or design change to avoid using reflection in this particular case
edit: the list will be used stuff like rendering modules in gui or calling execute method of the module from other user interface like command(simply find the module with matching name and execute it)
note: this is not how my code actually look like but a highly simplified version that give a similar idea
For a minimal-effort approach, java provides the built-in class java.util.ServiceLoader.
With this class you can obtain all available implementations of a service class (in this case Module) like
ServiceLoader moduleLoader = ServiceLoader.load(Module.class);
for (Module mod : moduleLoader) {
System.out.println(mod.name);
}
Note that I highlighted the term available in the previous sentence. In fact, the ServiceLoader implementation does not perform any advanced class-hierarchy lookup, instead it relies on a specific resource file named equal to the canonical name of the Module class.
META-INF/services/com.example.project.Module
com.example.project.impl1.Module1
com.example.project.impl2.Module2
Where each line of the file references an available implementation of Module.
Now, ServiceLoader is an implementation of the java SPI specification, which does not necessarily have to qualify perfectly for your use case. However, since its idea of service loading and providing is rather simple, you can easily build your own variant.
public class JsonServiceLoader<S> {
private Class<S> service;
private Map<String, String> serviceIdentifiers;
public static <T> JsonServiceLoader load(Class<T> service, ClassLoader cl) {
Map<String, String> serviceIdentifiers = new HashMap<>();
String name = "META-INF/json-services/" + service.getCanonicalName();
// TODO check for null references where necessary
for (Enumeration<URL> resources = cl.getResources(name); resources.hasMoreElements();) {
try (InputStream resource = resources.next().openStream()) {
for (Map.Entry<String, String> identifier : parseJson(resource).entrySet()) {
serviceIdentifers.merge(
identifer.getKey(),
identifer.getValue(),
(value1, value2) -> throw new ServiceConfigurationError(
"duplicate service identifier '" + identifier.getKey() + "'"
);
);
}
}
}
return new JsonServiceLoader<>(service, serviceIdentifiers);
}
private static Map<String, String> parseJson(InputStream resource) {
// TODO parse JSON data from the given stream using your favourite JSON facility
/*
* If you want to use a different style of resources, e.g. XML, this is the only
* location you have to change (you might want to rename 'json-services' though).
*/
return new HashMap<>();
}
private JsonServiceLoader(Class<S> service, Map<String, String> serviceIdentifiers) {
this.service = service;
this.serviceIdentifiers = serviceIdentifiers;
}
public Set<String> getServiceIdentifiers() {
return Collections.unmodifiableSet(serviceIdentifiers.keySet());
}
public S getService(String name) {
String className = serviceIdenfiers.get(name);
if (null == className) {
throw new IllegalArgumentException("invalid service identifier '" + name + "'");
}
// TODO improve error handling
try {
return service.cast(Class.forName(className).newInstance());
} catch(Exception exc) {
throw new ServiceConfigurationError("could not load service '" + name + "'", exc);
}
}
}
Your JSON resources could look like
META-INF/json-services/com.example.project.Module
{
"Mod1" : {
"class" : "com.example.project.impl1.Module1"
},
"Mod2" : {
"class" : "com.example.project.impl2.Module2"
}
}
allowing future extensions.
At this point, the Module class also does not need to know its name (the class member name) anymore, since you can always ask the service loader for an appropriate instance. (If you do so at some place in your code, you will already know for which name you just asked.) If so desired, you can also add more logic to this JsonServiceLoader, such as caching.
Ultimately, it all depends on how much information you want to process around your Module and how much of this information you want the Module implementations to handle and how much of it you want the service framework to handle.
Perhaps you can pass the list into the constructor of the parent class and add subclass itself to the list in the constructor method.
Just like this
public abstract class Module {
public final String name;
public Module(String name, List<Module> list) {
this.name = name;
list.add(this);
}
public abstract void execute();
}
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
new Mod1(MODULES);
}
Hope you can help me with this:
I have ...
a string list of class names called classNameList
a generic class Geography<T>
a static generic method <T> void read(Class<T> cl, Geography<T> geo)
I want to loop through the string class name list and call the generic method for each of these classes.
What I tried but obviously did not work:
for (int i = 0; i < classNameList.length; i++) {
Class<?> myClass = Class.forName(classNameList[i].getName());
Geography<myClass.newInstance()> geo;
read(myClass, geo);
}
Error: myClass.newInstance cannot be resolved to a type
My code runs perfectly for a single call of the generic function:
Geography<ExampleClass> ExampleGeo;
read(ExampleClass.class, ExampleGeo);
Any ideas how I could do this?
UPDATE:
Thanks for the helpful input, still it's hard for me to adopt it to my real code.
So this is the non simplyfied problem:
I do ready in shapefile-Data with a shapefileLoader, for each feature of the Shapefile a class (GuadAgent) is initialized with a predifined class (PlantWind). I have shapefiles in my input-directory with the names of the Classes their features do represent. I want Java to read in the shapefiles and create the respective agent class. (the agents are also placed in a context and a geography..)
Used classes are: ShapefileLoader, Geography, the other classes can be find at the same website
This part is in the main-method:
Geography<GuadAgent> guadGeography = GeographyFactoryFinder.createGeographyFactory(null).createGeography("guadGeography", context, new GeographyParameters<GuadAgent>());
Context<GuadAgent> context = new DefaultContext<GuadAgent>();
FileFilter filter = new FileFilter() {
#Override
public boolean accept(File file) {
return file.getName().endsWith(".shp"); // return .shp files
}
};
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
File folder = new File(shapefileDir);
File[] listOfFiles = folder.listFiles(filter);
for (File classFile : listOfFiles) {
try {
readForName(classFile,context,guadGeography);
} catch (ClassNotFoundException | MalformedURLException
| FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The static Method that reads in the names:
static <T> void readForName(File classFile, Context<GuadAgent> context,Geography<GuadAgent> guadGeography) throws ClassNotFoundException, MalformedURLException, FileNotFoundException {
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
String className = classFile.getName().split("\\.(?=[^\\.]+$)")[0];
File shapefile = null;
shapefile = new File(shapefileDir+classFile.getName());
if (!shapefile.exists()) {
throw new FileNotFoundException("Could not find the given shapefile: " + shapefile.getAbsolutePath());
}
switch (className) {
case "PlantWind":
ShapefileLoader<PlantWind> PlantWindLoader = new ShapefileLoader<PlantWind>(PlantWind.class,shapefile.toURI().toURL() , guadGeography, context);
PlantWindLoader.load();
PlantWindLoader.close();
System.out.println(context.getObjects(PlantWind.class).size());
break;
// Todo Add other Agent types
default:
break;
}
How can I get rid of the switch? Although their number is finit, there are very many different agents...
Unfortunately, there's no syntax close to your intention (nice idea though).
The basic problem is that Class.forName() returns an unknown Class<?>, so you need a cast somewhere. It's just a mater of where you put it.
I suggest this approach (which compiles) that bundles up doing a read() based on a class name:
static <T> void readForName(String className) throws ClassNotFoundException {
Class<T> myClass = (Class<T>) Class.forName(className);
Geography<T> geo = new Geography<T>(); // No code shown. Adjust as required
read(myClass, geo);
}
May I also suggest using the foreach loop syntax, for tidier code:
for (String className : classNameList) {
readForName(className.getName());
}
Creating instances from Generic Types at Runtime
I am not entirely clear on what you are trying to accomplish, but at first look it looks like the simplest solution is the best solution.
It could be solved with using a scripting environment ( Groovy, JavaScript, JRuby, Jython ) that could dynamically evaluate and execute arbitrary code to create the objects, but that got extremely convoluted and overly complex, just to create an object.
But unfortunately I think it has a very pedestrian solution.
As long as there is a predefined set of supported types, you can use a Factory pattern. Here I just leverage the Provider<>T interface from the javax.inject/com.google.inject package.
Q26289147_ProviderPattern.java
public class Q26289147_ProviderPattern
{
private static final List<String> CLASS_NAMES = ImmutableList.of("String", "Integer", "Boolean");
private static final Map<String, Provider<StrawManParameterizedClass>> PROVIDERS;
static
{
final ImmutableMap.Builder<String, Provider<StrawManParameterizedClass>> imb = ImmutableMap.builder();
for (final String cn : CLASS_NAMES)
{
switch (cn)
{
case "String":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<String> get() { return new StrawManParameterizedClass<String>() {}; }
});
break;
case "Integer":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
case "Boolean":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
default:
throw new IllegalArgumentException(String.format("%s is not a supported type %s", cn, Joiner.on(",").join(CLASS_NAMES)));
}
}
PROVIDERS = imb.build();
}
static <T> void read(#Nonnull final StrawManParameterizedClass<T> smpc) { System.out.println(smpc.type.toString()); }
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
#Override
public String toString() { return type.getRawType().getCanonicalName(); }
}
public static void main(final String[] args)
{
for (final String cn : CLASS_NAMES)
{
read(PROVIDERS.get(cn).get());
}
}
}
Disclaimer:
This is just a proof of concept example, I would never use a switch
statement like that in production code I would use a Strategy
Pattern or Chain of Responsibility Pattern to encapsulate the logic
of what type to create based on the ClassName key.
This initially looked like a generics problem, it isn't, it is a creation problem.
That said, you don't need to pass around instances of Class<?> you can get Generic Type information off of Parameterized classes at runtime with TypeToken from Guava.
You can even create instances of any generic type at runtime with TypeToken from the Guava library.
The main problem is this syntax isn't supported: Geography<myClass.newInstance()> geo; and I can't think of anyway to fake it other than the Provider implementation above.
Here is a straw man example of how to use TypeToken so that your
parameterized classes will always know their types!
Q26289147.java
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Notes:
Works great for classes that have a default no arg constructor.
Works better than using straight reflection if there are no default no arg constructors.
Should play well with Guice allowing you to use the ".getRawType()generatedClassto pass togetInstance()` of an Injector. have not tried this yet, I just thought of it!
You can use Class<T>.cast() to do casting that doesn't need #SuppressWarning("unchecked") all over the place.`
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
Update: I'm not sure what the Geography class is meant to do. If it needs a generically typed object, it might look like this:
public class Geography<T> {
private final T data;
public Geography(T data) {
this.data = Objects.requireNonNull(data);
}
}
If it needs a class, the constructor might look like this:
public class Geography<T> {
private final Class<T> dataClass;
public Geography(Class<T> cls) {
this.dataClass = Objects.requireNonNull(cls);
}
}
I would like to pass in a generic object into my method and have it get the property name, type, and value.
Here is my class
public class Login {
public String token;
public String customerid;
public Class1 class1;
public Class2 class2;
public class Class1 {
public Class3 class3;
public String string1;
public class Class3 {
public int int1;
public String string2;
public String string3;
}
}
public class Class2 {
public int int1;
public String string2;
public String string3;
}
}
I would like the output to look like this
User Preferences customerid - class java.lang.String - 586969
User Preferences token - class java.lang.String - token1
User Preferences string1 - class java.lang.String - string1Value
User Preferences string2 - class java.lang.String - string2Value
User Preferences string3 - class java.lang.String - string3Value
The code I have right now gives me issues. Here is the code:
try {
// Loop over all the fields and add the info for each field
for (Field field : obj.getClass().getDeclaredFields()) {
if(!field.isSynthetic()){
field.setAccessible(true);
System.out.println("User Preferences " + field.getName() + " - " + field.getType() + " - " + field.get(obj));
}
}
// For any internal classes, recursively call this method and add the results
// (which will in turn do this for all of that subclass's subclasses)
for (Class<?> subClass : obj.getClass().getDeclaredClasses()) {
Object subObject = subClass.cast(obj); // ISSUE
addUserPreferences(subObject, prefs);
}
}catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}catch(ClassCastException e) {
e.printStackTrace();
}
Getting the subObject, in this case Class1 or Class2, and passing it to the method is what Im having an issue with. I have tried with a class instead of an object but then I can't get the object from the class.
Is there anyway to cast the object I pass in to the subclass?
Thanks
You have a few options:
One option is to consider defining some interface that defines an object that provides user preferences, e.g.:
interface UserPreferenceProvider {
Map<String,Object> getUserPrefences();
}
Then you can make your classes implement that interface, e.g.:
public class Login implements UserPreferenceProvider {
...
public class Class1 implements UserPreferenceProvider {
...
public class Class2 implements UserPreferenceProvider {
...
}
}
}
Where their getUserPreferences() implementations return the preferences to write.
Then you can change addUserPreferences() to take a UserPreferenceProvider, and when you are traversing fields, check if you find a UserPreferenceProvider and, if so, cast it to that and pass it off to addUserPreferences().
This would more accurately represent your intentions, as well. I believe the fundamental issue here is you have these arbitrary objects that you're trying to work with, and while conceptually they have something in common, your code is not representing that concept; I know that's a bit vague but by not having your code reflect that, you are now faced with the awkward task of having to find a way to force your arbitrary objects to be treated in a common way.
A second option could be to create a custom annotation, e.g. #UserPreference, and use that to mark the fields you want to write. Then you can traverse the fields and when you find a field with this annotation, add it's single key/value to the user preferences (that is, operate on the fields themselves, instead of passing entire container classes to addUserPreferences()).
This may or may not be more appropriate than the first option for your design. It has the advantage of not forcing you to use those interfaces, and not having to write code to pack data into maps or whatever for getUserPreferences(); it also gives you finer grained control over which properties get exported -- essentially this shifts your focus from the objects to the individual properties themselves. It would be a very clean approach with minimal code.
A possible way to make this more convenient if you already have bean-style getters is to use e.g. Apache BeanUtils to get the values instead of rolling your own; but for your situation it's a pretty basic use of reflection that may not be worth an additional dependency.
Here is an example of getting names and values of the fields of an object tagged with a custom annotation. A second annotation is used to mark fields that contain objects that should be recursively descended into and scanned. It's very straightforward:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
// #UserPreference marks a field that should be exported.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface UserPreference {
}
// #HasUserPreferences marks a field that should be recursively scanned.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface HasUserPreferences {
}
// Your example Login class, with added annotations.
class Login {
#UserPreference public String token; // <= a preference
#UserPreference public String customerid; // <= a preference
#HasUserPreferences public Class1 class1; // <= contains preferences
public class Class1 {
#HasUserPreferences public Class2 class2; // <= contains preferences
#UserPreference public String string1; // <= a preference
public class Class2 {
public int int1; // <= not a preference
#UserPreference public String string2; // <= a preference
#UserPreference public String string3; // <= a preference
}
}
// Construct example:
public Login () {
token = "token1";
customerid = "586969";
class1 = new Class1();
class1.string1 = "string1Value";
class1.class2 = class1.new Class2();
class1.class2.string2 = "string2Value";
class1.class2.string3 = "string3Value";
}
}
public class ValueScanExample {
// Recursively print user preferences.
// Fields tagged with #UserPreference are printed.
// Fields tagged with #HasUserPreferences are recursively scanned.
static void printUserPreferences (Object obj) throws Exception {
for (Field field : obj.getClass().getDeclaredFields()) {
// Is it a #UserPreference?
if (field.getAnnotation(UserPreference.class) != null) {
String name = field.getName();
Class<?> type = field.getType();
Object value = field.get(obj);
System.out.println(name + " - " + type + " - " + value);
}
// Is it tagged with #HasUserPreferences?
if (field.getAnnotation(HasUserPreferences.class) != null) {
printUserPreferences(field.get(obj)); // <= note: no casts
}
}
}
public static void main (String[] args) throws Exception {
printUserPreferences(new Login());
}
}
The output is:
token - class java.lang.String - token1
customerid - class java.lang.String - 586969
string2 - class java.lang.String - string2Value
string3 - class java.lang.String - string3Value
string1 - class java.lang.String - string1Value
Note that "int1" is not present in the output, as it is not tagged. You can run the example on ideone.
The original basic annotation example can still be found here.
You can do all sorts of fun things with annotations, by the way, e.g. add optional parameters that let you override the field name in the preferences, add a parameter that lets you specify a custom object -> user preference string converter, etc.
I have figured out a simplistic way to do this. Anyone who has suggestions to make this better or has issues with the code please comment. The code below does work for me
try {
Class<?> objClass = obj.getClass();
List<Object> subObjectList = new ArrayList<Object>();
// Loop over all the fields and add the info for each field
for (Field field: objClass.getDeclaredFields()) {
if(!field.isSynthetic()){
if(isWrapperType(field.getType())){
System.out.println("Name: " + field.getName() + " Value: " + field.get(obj));
}
else{
if(field.getType().isArray()){
Object[] fieldArray = (Object[]) field.get(obj);
for(int i = 0; i < fieldArray.length; i++){
subObjectList.add(fieldArray[i]);
}
}
else{
subObjectList.add(field.get(obj));
}
}
}
}
for(Object subObj: subObjectList){
printObjectFields(subObj);
}
}catch(IllegalArgumentException e){
// TODO Auto-generated catch block
e.getLocalizedMessage();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.getLocalizedMessage();
}
The isWrapperType come from code I found in this stack overflow question. All i did was add String and int to the set.
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 .