How to get name from Java enum in Kotlin - java

I have a project that is written with both Java and Kotlin languages and recently I faced next issue.
Let's say we have MyMonth enum:
public enum MyMonth {
JAN("January"),
FEB("February");
private final String name;
MyMonth(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Then in Kotlin when we print the name of the month:
fun main() {
val month = MyMonth.JAN
println(month.name)
}
we get:
JAN
which is what is described in the documentation https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-enum/name.html, however actually the name should be January.
Is there a way to get a name in Kotlin that is specified in Java enum?
UPD: My Kotlin version is 1.3.30-release-170
UPD2: IDEA even shows me that name is coming from the getName() method defined in Java:
UPD3: When we explicitly use .getName() it works, however it looks kind of weird

You can call the getter directly instead of using the property syntax:
fun main() {
val name = MyMonth.JAN.getName()
println(name)
}

Your Java API has name as a private field. Nothing can access it, not in Java nor Kotlin.
If you want to access it, add e.g. the following to the Java API:
public String getMonthName() { return name; }
...and then access it from Kotlin as
val month = MyMonth.JAN.monthName

enum is now low usage. You can use IntDef instead of enums.
#IntDef(MyMonth.JAN, MyMonth.FEB)
#Retention(AnnotationRetention.SOURCE)
annotation class MyMonth {
companion object {
const val JAN = 0
const val FEB = 1
fun getDisplayString(toDoFilterTypes: Int): String {
return when (toDoFilterTypes) {
JAN -> "All"
FEB -> "Job"
else -> "N/A"
}
}
}
}

Related

Overriding get method for variable in class kotlin?

I have a model class in Java which I converted to data class in kotlin
public class VideoAssets implements Serializable {
#SerializedName("type")
#Expose
String type;
#SerializedName("mpeg")
#Expose
List<Mpeg> mpeg = null;
#SerializedName("hls")
#Expose
String hls;
#SerializedName("widevine")
#Expose
WideVine wideVine;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Mpeg> getMpeg() {
return mpeg;
}
public void setMpeg(List<Mpeg> mpeg) {
this.mpeg = mpeg;
}
public String getHls() {
hls = Macros.INSTANCE.replaceURl(hls);
return hls;
}
public void setHls(String hls) {
this.hls = hls;
}
public WideVine getWideVine() {
return wideVine;
}
public void setWideVine(WideVine wideVine) {
this.wideVine = wideVine;
}
}
As you see I want to change the value of variable hls when I retrieve it.
I created the data class as below
data class VideoAssets(#SerializedName("mpeg") #Expose
var mpeg: List<Mpeg> = emptyList(),
#SerializedName("hls")
#Expose
var hls: String,
#SerializedName("widevine")
#Expose
val wideVine: WideVine? = null) : Serializable
I am struggling here as how should I update the get method for data class.
After searching and taking reference from Override getter for Kotlin data class
I even created a non data class which doesn't seem to work
class VideoAssets(#SerializedName("mpeg") #Expose
var mpeg: List<Mpeg> = emptyList(),
#SerializedName("hls")
#Expose
val hlsUrl: String? = null,
#SerializedName("widevine")
#Expose
val wideVine: WideVine? = null) : Serializable {
val hls: String? = hlsUrl
get() = field?.let { Macros.replaceURl(it) }
}
Whenerver I try to retrieve videoAssets.getHls() it returns null while it should return the new value. The object videoAssets.gethlsUrl() has the value but not `videoAssets.getHls()' is always null.
Can someone point me what I am missing?
Here's your code:
val hls: String? = hlsUrl
get() = field?.let { Macros.replaceURl(it) }
So what this is doing, is creating a property called hls and giving it a backing field (a variable) called field. It initially sets that to whatever value for hlsUrl was passed into the constructor (might be null).
The getter code takes that value for field, and if it isn't null it calls that replaceURl function and returns the result, otherwise it returns null.
So if you set hlsUrl to null, field will always be null and the hls getter will always return null. Even if you update hlsUrl later (whicb I'm assuming you're doing, the code runs fine for me if I pass in a value to the constructor) the value of field is fixed at initialisation.
Also your Java code runs differently - when that gets the new value of hls, it stores that and uses it in the function call of the next get. You're never changing the value of field so your Kotlin code uses the initial value every time.
Technically you don't need the backing field since you're always effectively calling hlsUrl?.let { Macros.replaceURl(it) }. In that case you could make hlsUrl var and update that, or you can add a setter to your hls property and set the backing field when you get the new value
Here's the Kotlin page on properties, in case you haven't seen it!

Ambiguous method call with JvmSynthetic setter

I have a class with #JvmSynthetic setters in order to provide only fluent builder-like setters for Java clients:
class PersonBuilder {
#set:JvmSynthetic // hide a void setter from Java
var age: Int? = null
#set:JvmSynthetic
var name: String? = null
fun setAge(age: Int?) = apply { this.age = age }
fun setName(name: String?) = apply { this.name = name }
fun build() = Person(age!!, name!!)
}
And call it like this on the Java side:
new PersonBuilder()
.setAge(22) // <- "error"
.setName("Peter")
.build();
It does compile & run, but Android Studio shows the error "Ambiguous method call. Both setAge (Integer) in PersonBuilder and setAge (Integer) in PersonBuilder match" and doesn't provide any auto-suggestions and code formating after this "error".
I believe there is no need to use the #set:JvmSynthetic notation, just changing the visibility of variables should do the trick. Try this:
class PersonBuilder {
private var age: Int? = null
private var name: String? = null
fun setAge(age: Int) = apply { this.age = age }
fun setName(name: String) = apply { this.name = name }
fun build() = Person(age!!, name!!) //be careful!
}
Anyway, be careful with the build function. There are no guarantees that the functions setAge and setName will be called before it, so you can get a NullPointerException
Update
Another way to solve this problem by keeping #JvmSynthetic is to add the #JvmField notation as well. See this article for more information.

How can I define an enumerator with string values in JDL?

It is possible to define an enumerator with values in String in JDL as follows and with result in Java and Angular
JDL may be
enum LocaleLanguage {
ES_ES("es_ES"), ES_CA("es_CA"), EN_GB("en_GB"), EN_US("en_US")
loacle String
}
Java result may be
public enum LocaleLanguage
{
ES_ES("es_ES"),
ES_CA("es_CA"),
EN_GB("en_GB"),
EN_US("en_US");
private String locale;
LanguageLocale(String locale) {
this.locale = locale;
}
public String getLocale() {
return locale;
}
}
Angular
export const enum LocaleLanguage {
ES_ES = 'es_ES',
ES_CA = 'es_CA',
EN_GB = 'en_GB',
EN_US = 'en_US'
}
At this time this is not possible. A way to circumvent this issue would be to use translations and in the front end change the value to the desired String.

Make Enum.toString() localized

I'm developing an Android application and I want to know if I can set Enum.toString() multilanguage.
I'm going to use this Enum on a Spinner and I want to use multi language texts.
public class Types
{
public enum Stature
{
tall (0, "tall"),
average(1, "average"),
small(2, "small");
private final int stature;
private final String statureString;
Stature(int anStature, String anStatureString) { stature = anStature; statureString = anStatureString; }
public int getValue() { return stature; }
#Override
public String toString() { return statureString; }
}
}
I don't know how to use Context.getString() inside an Enum, and I have hardcoded "tall", "average" and "small" to test it. I have defined that enum inside on a helper class.
This how I use the enum on a Spinner:
mSpinStature.setAdapter(new ArrayAdapter<Stature>(mActivity, android.R.layout.simple_dropdown_item_1line, Stature.values()));
Do you know how can I do it?
I created a simple library which is a part of my big project (Xdroid):
compile 'com.shamanland:xdroid-enum-format:0.2.4'
Now you can avoid the same monkey-job (declaring field, constructor, etc) for all enumetations by using annotations:
public enum State {
#EnumString(R.string.state_idle)
IDLE,
#EnumString(R.string.state_pending)
PENDING,
#EnumString(R.string.state_in_progress)
IN_PROGRESS,
#EnumString(R.string.state_cancelled)
CANCELLED,
#EnumString(R.string.state_done)
DONE;
}
And then use the common Java approach - use extensions of class java.text.Format:
public void onStateChanged(State state) {
EnumFormat enumFormat = EnumFormat.getInstance();
toast(enumFormat.format(state));
}
strings.xml
<string name="state_idle">Idle</string>
<string name="state_pending">Pending</string>
<string name="state_in_progress">In progress</string>
<string name="state_cancelled">Cancelled</string>
<string name="state_done">Done</string>
Look here how to show Toast simply.
You can also compile a demo app from github.
Assume this resource path
String resourceBundlePath = "my.package.bundles.messages"
In package my.package.bundles you may have messages.properties, messages_en_US.properties etc.
Then, using
ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundlePath);
String messageKey = "myFirstMessage";
String message = resourceBundle.getMessage(messageKey);
message will contain the value of the messageKey property defined on messages.properties. If the current Locale is actually en_US you will get the value from messages_en_US.properties. If the current locale is something you do not have a properties file for the value will be from the default messages.properties
You can also call
ResourceBundle.getBundle(resourceBundlePath, myLocale);
but it is generally better to use the platform locale (have a look at jvm arguments -Duser.language, -Duser.country)
You can have a ResourceBundle for each enum you want to translate with keys the enum element names and use it in the toString() implementation of your enum:
#Override
public String toString() {
return resourceBudle.getString(super.toString());
}
I would leave enum as is and use the standard ResourceBundle approach http://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html using Enum.toString as the key
#Override
public String toString()
{
//java
return ResourceBundle.getBundle().getString(id);
//android?
App.getContext().getString(id);
}

IntelliJ Live Template: modified setters template

Is anyone know how to set up a live template in intellij for doing specialized setters
- i'm using v5, but I accept with pleasure knowledge for a more recent release) -
My first need is a firePropertyChange setter:
public final static String $PROPERTY$ = "$property$"
public void set$Property$($TYPE$ $property$) {
Object oldValue = this.$property$;
this.$property$ = $property$;
firePropertyChange($PROPERTY$, oldValue, $property$);
}
I have a semi-working version that generate the implementation with variables defined like this:
$property$ --> completeSmart()
$PROPERTY$ --> completeSmart()
My second need is a builder style setter that call the regular setter and then return this after the set:
public $THIS_TYPE$ with$Property$($TYPE$ $property$) {
set$Property$($property$);
return this;
}
For this one I have nothing really good: I still have to type a lot !
Any suggestion ?
Something like this
private $TYPE$ $NAME$;
public $THIS$ set$BNAME$($TYPE$ $NAME$) {
this.$NAME$ = $NAME$;
return this;
}
where
Type = complete()
NAME = suggestVariableName()
BNAME = capitalize(NAME)
THIS = className()
The only Problem ist that className will not work in nested classes as it will return "Outer$Inner" but it should work good enough.

Categories