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!
Related
This is a general issue/problem that I have come across. I wondered if anyone knows of any well suited design patterns or techniques.
private ExternalObject personObject;
private String name;
private int age;
private String address;
private String postCode;
public MyBuilderClass(ExternalObject obj)
this.personObject=obj;
build();
}
public build() {
setName(personObject.getName());
setAge(personObject.getAge());
setAddress(personObject.getAddress());
setPostCode(personObject.getPostCode());
.
.
. many more setters
}
The class above takes external objects from a queue and constructs MyBuilderClass objects.
A MyBuilderClass object is successfully built if all of the fields have been set to non-null non-empty values.
There will be many MyBuilderClass objects that cannot be built because data will be missing from the ExternalObject.
My problem, what is the best way to detect if an object has been correctly built?
I could check for null or empty values in the set methods and throw an exception. The problem with this approach is throwing exceptions is expensive and it will clogg the log files up because there will be many instances where an object cannot be built;
What other approaches could I use?
Correct me if I'm wrong: you are trying to find a good way to check if an object is valid, and if it is not, tell the client code about this without using an exception.
You can try a factory method:
private MyBuilderClass(ExternalObject obj)
this.personObject=obj;
build();
}
public static MyBuilderClass initWithExternalObject(ExternalObject obj) {
// check obj's properties...
if (obj.getSomeProperty() == null && ...) {
// invalid external object, so return null
return null;
} else {
// valid
MyBuilderClass builder = new MyBuilderClass(obj);
return builder.build();
}
}
Now you know whether an object is valid without using an exception. You just need to check whether the value returned by initWithExternalObject is null.
I wouldn't throw exceptions in cases that aren't exceptional. And as the only way for a constructor not to produce an object is to throw, you should not delay validation to the constructor.
I'd still recommend the constructor to throw if its results were to be invalid, but there should be a validation before that, so you don't even call the constructor with an invalid ExternalObject.
It's up to you if you want to implement that as a static method boolean MyBuilderClass.validate(ExternalObject) or by using the builder pattern with this validation.
Another approach for such a validation is to use java Annotations:
Make a simple annotaion class, let's say Validate:
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#interface Validate {
boolean required() default true;
}
then annotate the fields you want to be present as #Validate(required=true):
class MyBuilderClass {
private ExternalObject externalObject;
#Validate(required=true)
private String name;
#Validate(required=false) /*since it's a primitive field*/
private int age;
#Validate(required=true)
private String address;
#Validate(required=true)
private String postCode;
MyBuilderClass(ExternalObject externalObject) {
this.externalObject = externalObject;
build();
}
public void build() {
setName(personObject.getName());
setAge(personObject.getAge());
setAddress(personObject.getAddress());
setPostCode(personObject.getPostCode());
}
//.
//.
//. many more setters
}
And then add this method in the MyBuilderClass class, in order to check if your Object is built correctly:
public boolean isCorrectlyBuilt() throws IllegalAccessException {
boolean retVal = true;
for (Field f : getClass().getDeclaredFields()) {
f.setAccessible(true);
boolean isToBeChecked = f.isAnnotationPresent(Validate.class);
if (isToBeChecked) {
Validate validate = f.getAnnotation(Validate.class);
if (validate.required()/*==true*/) {
if (f.get(this) == null) {
retVal = false;
break;
/* return false; */
}
}
}
}
return retVal;
}
Here is an example of use :
public static void main(String[] args) throws Exception {
ExternalObject personObject = new ExternalObject();
personObject.setAge(20);
personObject.setName("Musta");
personObject.setAddress("Home");
personObject.setPostCode("123445678");
MyBuilderClass myBuilderClass = new MyBuilderClass(personObject);
System.out.println(myBuilderClass.isCorrectlyBuilt());
}
Output : true because the object is correctly built.
This will allow you to choose the fields that you want to be in the structure by reflection, without bringing those inherited from a base class.
As this previous answer suggests, here are 2 options either of which should be added after you have tried to set the variables.
use reflection to check whether any of the variables are null. (As mentioned in comments this will check all fields in this object but be careful with fields in any superclasses).
public boolean checkNull() throws IllegalAccessException {
for (Field f : getClass().getDeclaredFields())
if (f.get(this) != null)
return false;
return true;
}
perform a null check on each variable.
boolean isValidObject = !Stream.of(name, age, ...).anyMatch(Objects::isNull);
Previous answer
From what I've come across you could overwrite the equals method of your object and compare it with a valid example object. Its dirty and might only work in some cases.
Your approach is the best I could think of. Write a seperate method or class that has for example a static validate method. You could reuse it anywhere.
I'm currently working on a project that has multiple stages to set up a certain thing. In stage one, the user provides a Title, Description, and a required int value. I need to do two things with this data:
Take the title, and set it as the ActionBar title. This is not hard by any means. I've simply set the variable that the Title value was stored in as an extra on the intent, and retrieved it in the new activity, and set it using the .setTitle(); method on the ActionBar.
Here's the one I need help with...
I need to get that integer value transferred over so I can use it as the number returned by the SectionsPagerAdapter, so when it calls getCount(); it returns the value.
I can get the value inside of the same Class as the Title value, but cannot seem to get it in the SectionsPagerAdapter.
Any help is appreciated!
Alternately you can extend SectionsPagerAdapter and include a setter for that value or use a convenience constructor.
Something like this:
public class CustomPagerAdapter extends PagerAdapter {
private int mPageCount;
/**
*
* #param pageCount
*/
public CustomPagerAdapter(int pageCount) {
this.mPageCount = pageCount;
}
#Override
public int getCount() {
return mPageCount;
}
}
I do not know clearly your problem, but before you change page or when you have value, you can use this class:
Class SavedDave extends Application(){
private static String data;
//Contructor
....
public void SetData(String value){
this.data = value;
}
public String getData(){
return data;
}
}
By this class, you can set and get your value every where when application running, is it solve your problem?
Suppose your method in activity is named getTitle();
From adapter call it like context.getTitle();
where context would be one you pass to the constructor of adapter
private final Context context;
public DataPagerAdapter( Context context )
{
this.context = context;
}
Use Shared Preferences. This is the recommended way to do it in Android.
#SharedPref
public interface ExamplePrefs {
// The name will have default value of "Anonymous"
#DefaultString("Anonymous")
String name();
// The field age will have default value 42
#DefaultInt(42)
int age();
// The field address will have default value of "MyAddress"
#DefaultString("MyAddress")
String name();
// The field lastUpdated will have default value 0
long lastUpdated();
}
For your use case just create a class SectionsPagerPref as follows :
#SharedPref
public interface SectionsPagerPref {
#DefaultInt(0)
int getCount();
}
I want convert json string to one object.
The json looks like this:
{"receive":1413342268310}
And the object is like:
public class PositionBean {
private Long id;
private Date receive;
public void setReceive (Date receive) {
this.receive = receive;
}
public void setReceive (Long receive) {
this.receive = new Date (receive);
}
public Long getReceive () {
return receive.getTime ();
}
}
All the set and get methods I have to use in other class, so I can't delete one method.
When I invoke
objectMapper.readValue(str, PositionBean.class);
It prompt exception, the jackon don't know which method set, so I use #JsonIgnore, but I found the receive is null.
You can use annotation #JsonSetter to specify which method should be used as setter.
Example:
public class PositionBean {
private Long id;
private Date receive;
public void setReceive (Date receive) {
this.receive = receive;
}
#JsonSetter
public void setReceive (Long receive) {
this.receive = new Date (receive);
}
public Long getReceive () {
return receive.getTime ();
}
}
When you mark setter (e.g. setXXX) with #JsonIgnore it means that property XXX will be ignored.
From documentation:
For example, a "getter" method that would otherwise denote a property
(like, say, "getValue" to suggest property "value") to serialize,
would be ignored and no such property would be output unless another
annotation defines alternative method to use.
You can also use
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
This will not throw any mapping exception even if u dont have an appropriate field in the mapping class corresponding to a JSON field. Once configured u can use ur code for further processing.
objectMapper.readValue (str, PositionBean.class);
SOLUTION
I was adding a new instance of A to the list: aList.add( new A() ), whose name property is of course null, instead of adding the actual initialized instance. Sorry for the dumb question.
I have this A class of ProjectA which overrides its toString method to return a clone of its name property, like this:
public class A {
private String name;
#Override
public String toString() {
return new String(name);
}
}
I then export this library to a jar and import it into ProjectB and when I call a.toString() I get a NullPointerException that says there is an error exactly on the return line: return new String(name);.
However, if I put it like this:
public class A {
private String name;
#Override
public String toString() {
return name;
}
}
I don't get any exception but the String returned is null.
I built the jar (ProjectA) using Eclipse and imported it into ADT (ProjectB - Eclipse too).
NOTE:
I omitted the getters/setters intentionally for the sake of simplicity, but they're in there in the original code and I'm pretty sure I set the name property way before calling the toString() method. In fact, if I call the getName() method, the name is returned perfectly fine, but I'm using lists and I need the toString() method.
This is the part of the code where the List of A objects is created (ProjectA too):
ArrayList<A> aList = new ArrayList<Categoria>();
for (int i = 0; i < random.nextInt(3)+1; i++) {
A a = new A();
a.setId(0);
a.setName("Test name");
a.setDescription("Test desc.");
aList.add(a);
Log.d("app", "Created object A: "+a.getName()); // The name is displayed OK here
}
aList.trimToSize();
And this is the exact part of the code where the toString() method is called (ProjectA):
new ArrayAdapter<A>(
actionBar.getThemedContext(),
android.R.layout.simple_list_item_1,
android.R.id.text1,
DataAccess.getAList() // The ArrayAdapter class calls the toString method to populate a list
)
As you could see, I in fact verify the content of the name property via the getName() method and it is okay. I have to mention that the first approach (the on which uses new String(name), without an finals nor null checks) worked flawlessly on another project.
This is the (relevant part of the) DataAccess class:
public final class DataAccess {
private static final Data data;
public static Arrayist<A> getAList() {
return this.data.getAList();
}
}
When you invoke new String(name); it invokes the overloaded parameterized constructor of String shown below :
public String(String original) {
int size = original.count;
........
.........
}
As you can see the first line in the code tries to compute the length of the String passed to the constructor. Since in your case the String is null invoking the member variable count on that null reference throws NullPointerException.
Note : In the code where you create AList, i dont see you adding the object to the list i.e. AList.add(a); is missing
return new String(name);
Would fail on name being null. It could happen on A a = new A(); System.out.println(a):.
The used constructor String(String) is a relict of the beginning of Java, reminiscent of the C++ copy constructor.
In Java String is immutable, and Java does not need the copy constructor.
Two solutions:
return String.valueOf(name); // Would return "null" for null.
return name == null ? "" : name;
As far as the exception goes you are getting a null pointer as already explained by #Kakarot.
This line blows up
original.count
But if you want to save yourself from null checking etc and at the same time have a efficient toString() method than user some thing like this.
public class A {
private String name = null;
private String address = " Some place";
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
return sb.append(name).append(address).toString();
}
}
Using string builder is efficient and you can see the output for the above toString() even with null values.
null Some place
In java new String(null) results into NullPointerException.
#Override
public String toString() {
if(name!=null){
return name;
}else{
return "";
}
}
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
Summarizing excellent answers by Programmer Bruce and StaxMan:
Missing properties referenced by the constructor are assigned a default value as defined by Java.
You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
For questions such as this, I like to just write a sample program and see what happens.
Following is such a sample program.
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// {"name":"Fred","id":42}
String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
System.out.println(bar1);
// output:
// Bar: name=Fred, id=42
// {"name":"James"}
String jsonInput2 = "{\"name\":\"James\"}";
Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
System.out.println(bar2);
// output:
// Bar: name=James, id=0
// {"id":7}
String jsonInput3 = "{\"id\":7}";
Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
System.out.println(bar3);
// output:
// Bar: name=null, id=7
}
}
class Bar
{
private String name = "BLANK";
private int id = -1;
Bar(#JsonProperty("name") String n, #JsonProperty("id") int i)
{
name = n;
id = i;
}
#Override
public String toString()
{
return String.format("Bar: name=%s, id=%d", name, id);
}
}
The result is that the constructor is passed the default value for the data type.
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFooToo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
// {"name":null,"id":99}
String jsonInput1 = "{\"name\":null,\"id\":99}";
BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
System.out.println(barToo1);
// output:
// BarToo: name=null, id=99
// {"id":99}
String jsonInput2 = "{\"id\":99}";
BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
System.out.println(barToo2);
// output:
// BarToo: name=BLANK, id=99
// Interrogate barToo1 and barToo2 for
// the current value of the name field.
// If it's null, then it was null in the JSON.
// If it's BLANK, then it was missing in the JSON.
}
}
class BarToo
{
String name = "BLANK";
int id = -1;
#Override
public String toString()
{
return String.format("BarToo: name=%s, id=%d", name, id);
}
}
Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON
In addition to constructor behavior explained in #Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value.
Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.
Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.
I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?
(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:
public class Foo {
private final int number;
public Foo(#JsonProperty Integer number) {
if (number == null) {
this.number = 42; // some default value
} else {
this.number = number;
}
}
}
this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent
another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.