java.lang.UnsupportedOperationException: JsonObject - Not sure why - java

I have the following code:
JsonElement deviceConfig = null;
JsonObject status = getRestAPI().Connectivity().getDeviceStatus(device);
deviceConfig = status.get("deviceConfig");
if (deviceConfig == null || deviceConfig.isJsonNull()) {
deviceConfig = status.get("mConfig");
}
if (deviceConfig != null && !deviceConfig.isJsonNull()) {
if (!deviceConfig.getAsString().isEmpty()) {
break;
}
}
For some reasons, I get the following error:
java.lang.UnsupportedOperationException: JsonObject
at com.google.gson.JsonElement.getAsString(JsonElement.java:191)
In this line:
if (!deviceConfig.getAsString().isEmpty()) {
Any idea why I get this exception although I checked that the JSON is not null?

JsonElement Source code: https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/JsonElement.java
The JsonElement class is an abstract class, it's meant to be used through subclasses that provide further implementations, for which the abstract class isn't concrete enough.
The getAsString method exists, yes, but is implemented like this:
/**
* convenience method to get this element as a string value.
*
* #return get this element as a string value.
* #throws ClassCastException if the element is of not a {#link JsonPrimitive} and is not a valid
* string value.
* #throws IllegalStateException if the element is of the type {#link JsonArray} but contains
* more than a single element.
*/
public String getAsString() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
Which basically means: you are expected to provide an implementation in your subclass.
So, in order to get the result you desire, you'll need to cast the variable to your subclass before calling getAsString() on it.

Related

String values in Optional ofNullable

Imagine an Optional.ofNullable check assigning to a String:
String result = Optional.ofNullable(class1)
.map(Class1::getClass2)
.map(Class2::getResult);
Where getResult returns a String.
While I know this doesn't compile, I can fix it by either adding toString() or .orElse(""); to sort that.
As it stands, the error is:
Bad return type in method reference, cannot convert java.lang.String
to U
I understand adding orElse("") as that will assign result to an empty String.
But what's the benefit of adding toString() if something is null along the way? Or is that just to purely get it to compile?
The return type of map is Optional <U>, so to get a real value you should call for orElse with the return type of T.
This is the toString implementation if the Optional:
#Override
public String toString() {
return value != null
? String.format("Optional[%s]", value)
: "Optional.empty";
}
So, calling toString you'll never get the real value, but a value wrapped to Optional, while orElse will return you the default provided value.
Let's see the difference:
Integer i = 4;
String s = Optional.ofNullable(i)
.map(Objects::toString)
.toString();
System.out.println(s);
Output:
Optional[4]
With null:
Integer i = null;
String s = Optional.ofNullable(i)
.map(Objects::toString)
.toString();
System.out.println(s);
Output:
Optional.empty
While using orElse:
Integer i = null;
String s = Optional.ofNullable(i)
.map(Objects::toString)
.orElse("None");
System.out.println(s);
Output:
None
So you can see that there are different purposes of these methods.
And the answer to your comment:
"Is there a way to call get() and also call orElse() in the same chain?"
Integer i = 10;
String s = Optional.ofNullable(i)
.map(Objects::toString)
.orElse("None");
System.out.println(s);
Output:
10
You don't need to call get explicitly, the value will be fetched if not null;
/**
* If a value is present, returns the value, otherwise returns
* {#code other}.
*
* #param other the value to be returned, if no value is present.
* May be {#code null}.
* #return the value, if present, otherwise {#code other}
*/
public T orElse(T other) {
return value != null ? value : other;
}
I understand adding orElse("") as that will assign result to an empty
String.
It doesn't sound like you do understand it to me because that's not a good description of what's happening. Optional.orElse does the following: if the optional contains a value, return that value. If it doesn't contain a value, return whatever argument you've given it.
It's semantically equivalent to the following:
if (optional.ifPresent())
{
return optional.get();
}
else
{
return theArgument;
}
Calling toString, while it will satisfy the compiler, is not what you want to do. You are converting the Optional object itself to a String, not getting the String from inside! While your string will be included, this is only because of how the JDK developers have decided to implement toString. They could equally have not provided an implementation, leaving you with just the default behaviour.
Calling toString on an Optional should basically never be relied upon outside of logging. It's essentially just debugging information. If you do this, then information about the Optional wrapper will be printed as well, which is almost certainly not what you want.
System.out.println(Optional.empty()); // Optional.empty
System.out.println(Optional.of("foo")); // Optional[foo]
If you want result to be null if something along the way returns null then do orElse(null)
String result = Optional.ofNullable(class1)
.map(Class1::getClass2)
.map(Class2::getResult).orElse(null);

Why reflective Members must be copied before propagation?

If you look into the source code of getting reflective objects of fields, methods or constructors, their copies are returned. Lets take getting field as example:
/**
* Returns an array of {#code Field} objects reflecting all the fields
* declared by the class or interface represented by this
* {#code Class} object. This includes public, protected, default
* (package) access, and private fields, but excludes inherited fields.
*
* <p> If this {#code Class} object represents a class or interface with no
* declared fields, then this method returns an array of length 0.
*
* <p> If this {#code Class} object represents an array type, a primitive
* type, or void, then this method returns an array of length 0.
*
* <p> The elements in the returned array are not sorted and are not in any
* particular order.
*
* #return the array of {#code Field} objects representing all the
* declared fields of this class
* #throws SecurityException
* If a security manager, <i>s</i>, is present and any of the
* following conditions is met:
*
* <ul>
*
* <li> the caller's class loader is not the same as the
* class loader of this class and invocation of
* {#link SecurityManager#checkPermission
* s.checkPermission} method with
* {#code RuntimePermission("accessDeclaredMembers")}
* denies access to the declared fields within this class
*
* <li> the caller's class loader is not the same as or an
* ancestor of the class loader for the current class and
* invocation of {#link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to the package
* of this class
*
* </ul>
*
* #since 1.1
* #jls 8.2 Class Members
* #jls 8.3 Field Declarations
*/
#CallerSensitive
public Field[] getDeclaredFields() throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
return copyFields(privateGetDeclaredFields(false));
}
And
// Returns an array of "root" fields. These Field objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyField.
private Field[] privateGetDeclaredFields(boolean publicOnly) {
Field[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;
if (res != null) return res;
}
// No cached value available; request value from VM
res = Reflection.filterFields(this, getDeclaredFields0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicFields = res;
} else {
rd.declaredFields = res;
}
}
return res;
}
And
private static Field[] copyFields(Field[] arg) {
Field[] out = new Field[arg.length];
ReflectionFactory fact = getReflectionFactory();
for (int i = 0; i < arg.length; i++) {
out[i] = fact.copyField(arg[i]);
}
return out;
}
And in jdk.internal.reflect.ReflectionFactory
/** Makes a copy of the passed field. The returned field is a
"child" of the passed one; see the comments in Field.java for
details. */
public Field copyField(Field arg) {
return langReflectAccess().copyField(arg);
}
And in java.lang.reflect.Field
// For sharing of FieldAccessors. This branching structure is
// currently only two levels deep (i.e., one root Field and
// potentially many Field objects pointing to it.)
//
// If this branching structure would ever contain cycles, deadlocks can
// occur in annotation code.
private Field root;
And in java.lang.reflect.ReflectAccess (implementation of jdk
public Field copyField(Field arg) {
return arg.copy();
}
And finally back to java.lang.reflect.Field
/**
* Package-private routine (exposed to java.lang.Class via
* ReflectAccess) which returns a copy of this Field. The copy's
* "root" field points to this Field.
*/
Field copy() {
// This routine enables sharing of FieldAccessor objects
// among Field objects which refer to the same underlying
// method in the VM. (All of this contortion is only necessary
// because of the "accessibility" bit in AccessibleObject,
// which implicitly requires that new java.lang.reflect
// objects be fabricated for each reflective call on Class
// objects.)
if (this.root != null)
throw new IllegalArgumentException("Can not copy a non-root Field");
Field res = new Field(clazz, name, type, modifiers, slot, signature, annotations);
res.root = this;
// Might as well eagerly propagate this if already present
res.fieldAccessor = fieldAccessor;
res.overrideFieldAccessor = overrideFieldAccessor;
return res;
}
But why? Cannot we just simply access the root Field object and mess with it?
I'm not an expert with reflection, so there might be other reasons. But a Field is mutable (setAccessible()). Not returning a copy would mean that making it accessible in one part of the code would make it accessible everywhere, even in other code relying on the field not being accessible, or not allowed tomake it accessible.

Why A.class is null in run time?

I meet a problem when using A.class in program and it return java.lang.NullPointerException at run-time
This is my snipped code:
public synchronized boolean isDeviceEqual(IDevice dev) {
............
if( isDeviceInstanceOf(SimpleDevice.class) ) {
return dev instanceof IDevice
&& XXX();
}
............
}
public boolean isDeviceInstanceOf(Class cls) {
return cls.isAssignableFrom(mDeviceClass);
}
and NPE
java.lang.NullPointerException
at xxx/library/DeviceDescriptor.isDeviceInstanceOf(Ljava/lang/Class;)Z (:0:5)
at xxx/library/DeviceDescriptor.isDeviceEqual(LIDevice;)Z (:0:6)
with above NPE, it means that cls is null in this case but I can't explain why this happens so can anybody help me?
cls.isAssignableFrom(mDeviceClass) will throw a NullPointerException if mDeviceClass, which must be the case here, since you are sure that cls is not null.
/**
* Determines if the class or interface represented by this
* <code>Class</code> object is either the same as, or is a superclass or
* superinterface of, the class or interface represented by the specified
* <code>Class</code> parameter. It returns <code>true</code> if so;
* otherwise it returns <code>false</code>. If this <code>Class</code>
* object represents a primitive type, this method returns
* <code>true</code> if the specified <code>Class</code> parameter is
* exactly this <code>Class</code> object; otherwise it returns
* <code>false</code>.
*
* <p> Specifically, this method tests whether the type represented by the
* specified <code>Class</code> parameter can be converted to the type
* represented by this <code>Class</code> object via an identity conversion
* or via a widening reference conversion. See <em>The Java Language
* Specification</em>, sections 5.1.1 and 5.1.4 , for details.
*
* #param cls the <code>Class</code> object to be checked
* #return the <code>boolean</code> value indicating whether objects of the
* type <code>cls</code> can be assigned to objects of this class
* #exception NullPointerException if the specified Class parameter is
* null.
* #since JDK1.1
*/
public native boolean isAssignableFrom(Class<?> cls);
It seems strange that in isDeviceEqual(IDevice dev) you are not doing anything with dev. I don't know what mDeviceClass is, but perhaps it should be assigned dev.getClass() somewhere.

Does this method create an exact duplicate? (A copy method in java)

I have a homework to write a test for the following method in other to show that the Object and its duplicate ,which has been created using this method, are equal.
/**
* Creates a new object that is a duplicate of this instance of
* Parameter.
* <p>
* The duplication is a "deep copy" in that all values contained in the
* Parameter are themselves duplicated.
*
* #return The new duplicate Parameter object.
*/
public Parameter copy( )
{
Parameter result = new Parameter( );
result.setName( getName( ) );
for ( int index = 0; index < getNumberOfValues( ); index++ )
{
result.addValue( getValue( index ).copy( ) );
}
return result;
}
I wrote different methods, but each time the result shows that they are not equal. One of my tests:
#Test
public void testCopy() {
Parameter param = new Parameter();
Value val1 = new Value();
//val1.setName("Hi!");
//param.addValue(val1);
Parameter param2 = param.copy();
Parameter expected = param;
Parameter actual = param2;
assertEquals(param, param2);
}
But it seems that this method doesn't create and exact duplicate of param.
Would you please guide me?
And this is the copy method for value:
/**
* Creates a new Value object that is a duplicate of this instance.
*
* #return The new duplicate Value object.
*/
public Value copy( )
{
Value newValue = new Value( );
newValue.setName( getName( ) );
return newValue;
}
Your Parameter class needs an equals method(). Without this java runs equality based on reference(the default behavior of Object.equals() ), hence your observation.
Your equals method may look like:
public boolean equals(Object o)
{
Parameter p = (Parameter)o;
return this.getName().equals(p.getNames) && this.getNumberOfValues() == p.getNumberOfValues() &&B this.values().equals(p.values());
}
The equals method for the class Object (described here) states clearly that the references need to the refer to the same object in order to return true. In your case, the copy method correctly creates a new Parameter and afterwards a new Value in order to create the copy object. Hence, the existing equals() method will always return false.
In order to correctly test, you need to do what the comments stated, that is, create a new equals() method that will override the existing one in the Object class, both in the Parameter and in the Value classes. Your method should test whether the content (the name, in each object) of each object is the same, or any other semantic that you will deem fit according to your application.

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this:
Class arrayOfFooClass = java.lang.reflect.Array.newInstance(fooClass, 0).getClass();
Is there a way to do this without creating the new instance?
Since Java 12
Class provides a method arrayType(), which returns the array type class whose component type is described by the given Class. Please be aware that the individual JDK may still create an instance of that Class³.
Class<?> stringArrayClass = FooBar.arrayType()
Before Java 12
If you don't want to create an instance, you could create the canonical name of the array manually and get the class by name:
// Replace `String` by your object type.
Class<?> stringArrayClass = Class.forName(
"[L" + String.class.getCanonicalName() + ";"
);
But Jakob Jenkov argues in his blog that your solution is the better one, because it doesn't need fiddling with strings.
Class<?> stringArrayClass = Array.newInstance(String.class, 0).getClass();
³ Thanks for the hint to Johannes Kuhn.
Since Java 12, there is the arrayType() method on java.lang.Class. With that:
Class<?> arrayOfFooClass = fooClass.arrayType();
The implementation of Class.arrayType() just calls Arrays.newInstance(this, 0).getClass().
You can get it using the class name.
Just make sure you get the class using it's ClassLoader
Class klass = yourClass;
boolean init = wantToDoStaticOperations;
Class.forName("[L" + klass.getName() + ";", init, klass.getClassLoader());
So, I for one, like fiddling with Strings. So, here's a more general solution that takes that approach, and still works with arbitrary class types. It is certainly more fiddly than your answer, but anyways, it's more complex to make it generic than the accepted answer gives it credit for, so here is the complete set of code to make it work:
/**
* Returns the name of the class, as the JVM would output it. For instance, for an int, "I" is returned, for an
* array of Objects, "[Ljava/lang/Object;" is returned. If the input is null, null is returned.
*
* #param clazz
* #return
*/
public static String getJVMName(Class clazz) {
if(clazz == null) {
return null;
}
//For arrays, .getName() is fine.
if(clazz.isArray()) {
return clazz.getName().replace('.', '/');
}
if(clazz == boolean.class) {
return "Z";
} else if(clazz == byte.class) {
return "B";
} else if(clazz == short.class) {
return "S";
} else if(clazz == int.class) {
return "I";
} else if(clazz == long.class) {
return "J";
} else if(clazz == float.class) {
return "F";
} else if(clazz == double.class) {
return "D";
} else if(clazz == char.class) {
return "C";
} else {
return "L" + clazz.getName().replace('.', '/') + ";";
}
}
/**
* Generically and dynamically returns the array class type for the given class type. The dynamic equivalent of
* sending {#code String.class} and getting {#code String[].class}. Works with array types as well.
* #param clazz The class to convert to an array type.
* #return The array type of the input class.
*/
public static Class<?> getArrayClassFromType(Class<?> clazz) {
Objects.requireNonNull(clazz);
try {
return Class.forName("[" + getJVMName(clazz).replace('/', '.'));
} catch(ClassNotFoundException ex) {
// This cannot naturally happen, as we are simply creating an array type for a real type that has
// clearly already been loaded.
throw new NoClassDefFoundError(ex.getMessage());
}
}
Note that this is code from an existing library I wrote, which is why I make use of the getJVMName method. It could probably be modified to keep the dots instead of the /, but given that's how it works, I convert back and forth between the two methods. Anyhow, this works with any class, including nested array types.
Class stringArrayOfClass = String[].class;

Categories