How to use getResources.getString(R.string) in an Interface? - java

I have an interface that has a static key. And I wanted to retrieve this key from a string file, but if I just put R.string.key, it shows incompatible types, because it retrieves the integer value, and if I put R.string.key + "", it becomes a string but retrieves the String file id, it would be best to use getResources.getString (R.string.key), but there is no way to use the getResources.getString method in the Interface.
Working:
public interface NotificacaoService {
#Headers({"34853485734",
"Content-Type:application/json"}) #POST("send")
Call<NotificacaoDados> salvarNotificacao(#Body NotificacaoDados notificacaoDados);
}
I want to leave it like this:
public interface NotificacaoService {
#Headers({getResources.getString(R.string.key),
"Content-Type:application/json"}) #POST("send")
Call<NotificacaoDados> salvarNotificacao(#Body NotificacaoDados notificacaoDados);
}

Sorry, what you want is not possible. Annotation parameters need to be constants.
One solution is to switch from string resources to BuildConfig. Use buildConfigField in your Gradle script to define your key (buildConfigField "String", "API_KEY", "\"34853485734\""). Then, you can reference that generated constant in your interface (e.g.,BuildConfig.API_KEY`).
Alternatively — if this interface is for Retrofit — you could add your headers via an OkHttp interceptor, instead of via a #Headers annotation.

Related

How to make one annotation imply another in Java or Kotlin

I am using Retrofit and Kotlin to build an API client. For accessibility, I will provide examples in Java.
There are a few methods in this API that require a JSON body with only one parameter. I made an annotation #SingletonBody(String) that tells a call adapter to wrap the argument in a Map<String,Object>.
For example, to rename an account we PUT a A simple json body.
curl -X PUT https://api.example.com/account/whatever -d '{ "name": "the new name for the account" }'
I want to pass a String name argument for simplicity rather than a Map. I could accomplish this by creating a default method that delegates to another method.
public interface AccountApi {
// Inconvenient delegate
#PUT("/accounts/{accountId}")
Call<Void> renameAccount(#Path("accountId") String accountId, #Body Map<String,Object> body);
// Convenient wrapper
default Call<Void> renameAccount(String accountId, String name) {
return renameAccount(accountId, Collections.singletonMap("name", name));
}
}
This felt messy to me. My #SingletonBody annotation allows me to declare only one method.
public interface AccountApi {
#PUT("/accounts/{accountId}")
Call<Void> renameAccount(#Path("accountId") String accountId,
#Body #SingletonBody("name") String name);
}
TL;DR
My #SingletonBody annotation necessarily implies Retrofit's #Body annotation. How can I tell the complier to infer #Body from #SingletonBody such that I don't need to write both? Perhaps with an annotation preprocessor?
My instinct was to extend Retrofit's annotation, but the JVM (Kotlin too) does not allow annotations to have explicit superclasses or superinterfaces.

nameof equivalent in Java

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"

Map a collection with parameter with mapstruct

To map a certain object with mapstruct I need some custom post processing which needs an additional parameter to do it's work:
#Mapper
public abstract class AlertConfigActionMapper {
#Mappings({ #Mapping(target = "label", ignore = true)})
public abstract AlertConfigActionTO map (AlertConfigAction action, Locale userLanguage);
#AfterMapping
public void setLabel (AlertConfigAction action, #MappingTarget AlertConfigActionTO to, Locale userLanguage) {
for (AlertConfigActionLabel label : action.getAlertConfigActionLabels()) {
if (label.getLanguage().equals(userLanguage)) {
to.setLabel(label.getLabel());
break;
} else if (label.getLanguage().equals(Locale.ENGLISH)) {
to.setLabel(label.getLabel());
}
}
}
}
This works just fine.
The problem starts when I add following method to this mapper:
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, Locale userLanguage);
I need to pass this parameter (userLanguage) as well but mapstruct seems to 'break down' in this case: I generates following code for this part (which naturally gives a compilation error):
#Override
public List<AlertConfigActionTO> mapList(List<AlertConfigAction> actions, Locale userLanguage) {
if ( actions == null && userLanguage == null ) {
return null;
}
List<AlertConfigActionTO> list = new List<AlertConfigActionTO>();
return list;
}
I'm sure it is related to the parameter since if I remove it (from all mapping methods) then the mapList method is generated correctly.
What is needed to be done to allow custom parameters in this case?
What you describe is not possible (yet). Could you open a feature request in our issue tracker? We should provide means of denoting parameters as some sort of "context" which is passed down the call stack.
As a work-around for the time being, you might take a look at using a ThreadLocal which you set before invoking the mapping routine and which you access in your after-mapping customization. It's not elegant - and you need to make sure to clean up the thread local to avoid memory leaks - but it should do the trick.
I know that this question is quiet old, but I run into this issue, and starting at version 1.2 of mapstruct you can resolve it using #Context
So declaring the mapping for the list need to be like this :
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, #Context Locale userLanguage);
Now, you juste need to add another non abstract mapping like this :
public AlertConfigActionTO mapConcrete (AlertConfigAction action, #Context Locale userLanguage){
return map (action, userLanguage);
}
I don't think it is possible. At least not that way. Problem is that you prepare interface/abstract class - and rest is done by the engine. And that engine expects methods with one parameter... There are decorators, but they go the same way. I would try to inject language. Create bean, mark it as session scoped, and find out. With Spring, you would use ScopedProxyMode for that... Not sure how that goes with CDI.
Other option is more workaround, then solution - maybe that AlertConfigAction can pass that information?

How to use Custom type for #PathParam?

I want to use non spring bean class object as parameter for jersey web service class method. But it is giving missing dependency error at build time.
My code is:
#Component
#Path("/abcd")
public class ActorServiceEndpoint {
#POST
#Path("/test/{nonspringBean}")
#Produces(MediaType.APPLICATION_XML)
public void addActor(#PathParam("nonspringBean") MyNonSpringBeanClass nonspringBean){
}
}
The thing is path parameters come in String form. As per the specification, if we want the have a custom type be injected as a #PathParam, the custom class, should have one of three things:
A public static valueOf(String param) that returns the type
A public static fromString(String param) that returns the type
Or a public constructor that accepts a String
Another option implement a ParamConverter. You can see an example here.
If you don't own the class (it's a third-party class that you can't change) then your only option is to use the ParamConverter/ParamConverterProvider pair.
In either of these cases you'll want to construct the instance accordingly by parsing the String either in the constructor or in one of the above mentioned methods. After doing this, the custom type can be made a method parameter with the annotation.
The same holds true for other params, such as #FormParam, #HeaderParam, #QueryParam, etc.
It would help if you gave a bit more details of the error you're getting, but I see two problems with your code snippet:
The correct Spring annotation is #PathVariable, #PathParam is probably from another package. This doesn't apply as I guess you're using JAX-RS, not Spring annotations.
I'm not sure what converters are applied to path variables, but in any case it would need to have one for MyNonSpringBeanClass. I would take a String parameter and then instantiate MyNonSpringBeanClass myself in the function body.

Bean with generic field that is not always used

This is a curious situation: I have a bean like this that store some information and I need generics, because the field "data" can contain different types of data.
public class Request<T> {
private String name;
private Integer code;
private T data;
/* Getter and setters */
}
The fact is that "data" is not always used and sometimes it can be null. And if I want to avoid raw types I have to define a type anyway, like this:
Request<Object> req = ....
Is there a better way of doing that, where I can both 1) Avoid raw types 2) Have a generic data field in my request objects ???
If you don't mind the request type, use Request<?> in your declaration.
If the request is empty (meaning there is no type, which can be set as data), declare it as Request<Void>
You could always use the Void type, e.g.
Request<Void> req = ...
or a wildcard:
Request<?> req = ...
Maybe you should consider to change object hierarhy. If you dont use data in some cases, maybe you should have two objects:
class Request {
private String name;
private Integer code;
/* Getter and setters */
}
class DataRequest<T> extends Request {
private T data;
/* Getter and setters */
}
and use:
Request req1 = new Request();
Request req2 = new DataRequest<String>();
Maybe you should think in a different way: In your case a Request not always has associated data. Modelling this with null-values is a bad decision, because you have to check for this every time you want to use your data. At some point you want to handle Request without data in a different way then Request with data.
Maybe you should make your Request to an interface containing Methods like getName() and getCode() and create two concrete classes RequestWithData<T> and RequestWithoutData implementing this interface. Then you can check on creation of an RequestWithData<T>-instance, if a non-null data is provided. Furthermore, you can express in your method signature that you require a Request with data or without data. This leads to a more clean design and avoids your problem.
Use the new 'Optional' type in Java 8. It was made specifically for cases like these. If you cannot, for whatever reason, use Java 8 yet, the Google Guava library also implements that type. Check this example : https://gist.github.com/9833966

Categories

Resources