I have a library, I have to tag a class with certain field dynamically (code generation) and I don't want the meta-data field names I generate to clash with user-defined field names.
Using JavaScript, we can use ES6 Symbols to do this. We can create getters/setters and retrieve fields using Symbols and in this way prevent name-clashing.
So using JS, it might look like:
export class Foo {
static libraryDefinedField = Symbol('lib.defined')
userDefinedField = 'whatev';
setLibraryDefinedField(v){
this[Foo.libraryDefinedField] = v;
}
getLibraryDefinedField(v){
return this[Foo.libraryDefinedField];
}
}
is there a way to do this with Java somehow - create instance or static fields on a class that won't conflict with user-defined fields?
Note that using JS, if there were user-generated static field properties and we wanted to prevent nameclashing, we might do this:
// put the symbol outside the class, so even static properties won't conflict
const libraryDefinedField = Symbol('lib.defined');
export class Foo {
userDefinedField = 'whatev';
setLibraryDefinedField(v){
this[libraryDefinedField] = v;
}
getLibraryDefinedField(v){
return this[libraryDefinedField];
}
}
Java has no notion of symbols the way ES6 has.
If you simply want to "tag" a class, why not consider making the class implement an (possibly empty) interface? Class and interface names are unique.
Related
I went through some of the samples that have used libraries to generate bean classes from JSON, XML etc. What I would like to know is, whether there's a way to dynamically generate a java bean class, with the parameters I give?
For example if I give an array of Strings as arguments which would represent the properties of the Pojo class for now, how can I generate the POJO?
Arguments: {"field1", "field2", "field3"}
Generate POJO would be:
public class TestBean {
private String field1;
TestBean() {
}
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
}
It should be the same for field2 and field3 as well.
Here I'm assuming that all the properties above are String and the class name is constant for now. Is there any way I can achieve this? Thanks in advance.
The problem with generating an actual Java class at runtime is that there is no way you can access it using standard Java syntax as the compiler doesn't know about it.
In practice therefore, most people just use a map in this circumstance. The only case I can think where you would need to generate a real class is where there is some other code you can't change that requires a Java object and inspects it dynamically using reflection or otherwise.
If you don't need this you are better off using a map, or possibly some utility class designed to emulate a Java Bean.
The Apache BeanUtils package provides the DynaBean interface to implement dynamic Java Beans. That said, the classes are only recognised as beans if accessed from the rest of the BeanUtils package.
There are several subclasses depending on what you want, for example, LazyDynaBean:
DynaBean myBean = new LazyDynaBean();
myBean.set("myProperty", "myValue");
I'm implementing a plugin in which I need to add extends clause for an existing class.
I have PsiClass instance representing say MyClass.
There is an API that allows to get all the classes that MyClass extends:
PsiReferenceList extendsList = psiClass.getExtendsList()
And theoretically I can add something to it and that will work.
Problem: PsiReferenceList.add() consumes PsiElement and I don't know how to create an object of PsiElement having fully qualified name of the class I want to use.
More specifically, how to transform string com.mycompany.MyAbstractClass to PsiElement representing this class?
Update:
I managed to achieve the result using the following logic:
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiReferenceList extendsList = aClass.getExtendsList();
PsiShortNamesCache instance = PsiShortNamesCache.getInstance(project);
PsiClass[] abstractClasses = instance.getClassesByName(
"MyAbstractClass",
GlobalSearchScope.allScope(project)
);
PsiJavaCodeReferenceElement referenceElement = factory
.createClassReferenceElement(abstractClasses[0]);
extendsList.add(referenceElement);
It works but I guess there should be more optimal way.
You can make a String which is the code you want to generate, like
String code = "class A extends B { }"
Then, use this code to convert text into PsiElement:
PsiElement fromText(String code, Project project) {
return PsiFileFactory
.getInstance(project)
.createFileFromText(JavaLanguage.INSTANCE, code)
.getFirstChild()
}
And you'll get the corresponding PsiElement.
Then, myClass.replace(fromText(code)).
BTW you can also do classNamePsiElement.addAfter(fromText("extends Xxx")) which is considered more efficient.
C# 6.0 introduced the nameof() operator, that returns a string representing the name of any class / function / method / local-variable / property identifier put inside it.
If I have a class like this:
class MyClass
{
public SomeOtherClass MyProperty { get; set; }
public void MyMethod()
{
var aLocalVariable = 12;
}
}
I can use the operator like this:
// with class name:
var s = nameof(MyClass); // s == "MyClass"
// with properties:
var s = nameof(MyClass.OneProperty); // s == "OneProperty"
// with methods:
var s = nameof(MyClass.MyMethod); // s == "MyMethod"
// with local variables:
var s = nameof(aLocalVariable); // s == "aLocalVariable".
This is useful since the correct string is checked at compile time. If I misspell the name of some property/method/variable, the compiler returns an error. Also, if I refactor, all the strings are automatically updated. See for example this documentation for real use cases.
Is there any equivalent of that operator in Java? Otherwise, how can I achieve the same result (or similar)?
It can be done using runtime byte code instrumentation, for instance using Byte Buddy library.
See this library: https://github.com/strangeway-org/nameof
The approach is described here: http://in.relation.to/2016/04/14/emulating-property-literals-with-java-8-method-references/
Usage example:
public class NameOfTest {
#Test
public void direct() {
assertEquals("name", $$(Person.class, Person::getName));
}
#Test
public void properties() {
assertEquals("summary", Person.$(Person::getSummary));
}
}
Sadly, there is nothing like this. I had been looking for this functionality a while back and the answer seemed to be that generally speaking, this stuff does not exist.
See Get name of a field
You could, of course, annotate your field with a "Named" annotation to essentially accomplish this goal for your own classes. There's a large variety of frameworks that depend upon similar concepts, actually. Even so, this isn't automatic.
You can't.
You can get a Method or Field using reflection, but you'd have to hardcode the method name as a String, which eliminates the whole purpose.
The concept of properties is not built into java like it is in C#. Getters and setters are just regular methods. You cannot even reference a method as easily as you do in your question. You could try around with reflection to get a handle to a getter method and then cut off the get to get the name of the "property" it resembles, but that's ugly and not the same.
As for local variables, it's not possible at all.
You can't.
If you compile with debug symbols then the .class file will contain a table of variable names (which is how debuggers map variables back to your source code), but there's no guarantee this will be there and it's not exposed in the runtime.
I was also annoyed that there is nothing comparable in Java, so I implemented it myself: https://github.com/mobiuscode-de/nameof
You can simply use it like this:
Name.of(MyClass.class, MyClass::getProperty)
which would just return the String
"property"
It's also on , so you can add it to your project like this:
<dependency>
<groupId>de.mobiuscode.nameof</groupId>
<artifactId>nameof</artifactId>
<version>1.0</version>
</dependency>
or for Gradle:
implementation 'de.mobiuscode.nameof:nameof:1.0'
I realize that it is quite similar to the library from strangeway, but I thought it might be better not to introduce the strange $/$$ notation and enhanced byte code engineering. My library just uses a proxy class on which the getter is called on to determine the name of the passed method. This allows to simply extract the property name.
I also created a blog post about the library with more details.
Lombok has an experimental feature #FieldNameConstants
After adding annotation you get inner type Fields with field names.
#FieldNameConstants
class MyClass {
String myProperty;
}
...
String s = MyClass.Fields.myProperty; // s == "myProperty"
I have a class that I wish to protobuf. in that class one of the fields is an enum (in a class of it's own). Can I avoid defining an identical enum value in my .proto file ? Or will I have to manually make sure the enum definition in the java code is the same as in the .proto file?
java code:
public enum Location {
UNDEF(0),HOME(1), WORK(2);
...
}
.proto file corresponding code:
message Address{
enum location {
UNDEF = 0;
HOME = 1;
WORK = 2;
}
optional location addressLocation;
...
}
The best solution for keeping things like this in sync is often code generation; determine which of your definitions is the Single Point of Truth, and create the others from it. Protocol Buffers has built-in support for Java code generation with nifty features like automatically handling duplicate enum values.
I have classes that are automatically generated in Java. I want to add a method to that class (in another file) so that I can add additional functionality without changing the generated file. The idea being that if I have to recreate the generated file, I won't lose my new functionality.
In Objective-c I know this is called categories, and in JavaScript you can append the object's prototype, but I am unaware of how to do this in Java or what it is called.
This is not supported in java.
partial classes/partial class file
One thing you can do is inherit the generated class (if it is not final) and add your methods.
You could just use composition, ie:
public class JasonString {
String wrapped;
public JasonString() {
wrapped = new String();
}
public String toString() {
return wrapped.toLowerCase().toString();
}
// other methods of wrapped class you're using should just call the corresponding method in wrapped.
}
If you do not have access to the generated class you could try to extend it (if generated class is not final) and add new methods to sub-class... Java doesn't support dynamic attributes as JavaScript.
I agree with #Itay I used to have auto-generated classes from Ibatis and the best way to go about with your problem is to extend all the generated classes and add the functionality that you want.