dotnet / java-interop

Java.Interop provides open-source bindings of Java's Java Native Interface (JNI) for use with .NET managed languages such as C#
Other
201 stars 53 forks source link

How to correctly modify the data type of an interface member as a generic property #942

Closed SmallChi closed 2 years ago

SmallChi commented 2 years ago

generated

// Metadata.xml XPath interface reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/interface[@name='Mutable']"
[Register ("org/apache/commons/lang3/mutable/Mutable", "", "Org.Apache.Commons.Lang3.Mutable.IMutableInvoker")]
[global::Java.Interop.JavaTypeParameters (new string [] {"T"})]
public partial interface IMutable : IJavaObject, IJavaPeerable {
    global::Java.Lang.Object? Value {
    // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/interface[@name='Mutable']/method[@name='getValue' and count(parameter)=0]"
    [Register ("getValue", "()Ljava/lang/Object;", "GetGetValueHandler:Org.Apache.Commons.Lang3.Mutable.IMutableInvoker, XMMauiLibDemo")]
    get; 

    // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/interface[@name='Mutable']/method[@name='setValue' and count(parameter)=1 and parameter[1][@type='T']]"
    [Register ("setValue", "(Ljava/lang/Object;)V", "GetSetValue_Ljava_lang_Object_Handler:Org.Apache.Commons.Lang3.Mutable.IMutableInvoker, MauiLibDemo")]
    set; 
    }
}

public partial class MutableShort : global::Java.Lang.Number, global::Java.Lang.IComparable, global::Org.Apache.Commons.Lang3.Mutable.IMutable {
        public virtual unsafe global::Java.Lang.Short? Value
        {
            // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/class[@name='MutableShort']/method[@name='getValue' and count(parameter)=0]"
            [Register("getValue", "()Ljava/lang/Short;", "GetGetValueHandler")]
            get
            {
                const string __id = "getValue.()Ljava/lang/Short;";
                try
                {
                    var __rm = _members.InstanceMethods.InvokeVirtualObjectMethod(__id, this, null);
                    return global::Java.Lang.Object.GetObject<global::Java.Lang.Short>(__rm.Handle, JniHandleOwnership.TransferLocalRef);
                }
                finally
                {
                }
            }
        }

        // This method is explicitly implemented as a member of an instantiated Org.Apache.Commons.Lang3.Mutable.IMutable
        global::Java.Lang.Object? global::Org.Apache.Commons.Lang3.Mutable.IMutable.Value {
            // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/interface[@name='Mutable']/method[@name='getValue' and count(parameter)=0]"
            [Register ("getValue", "()Ljava/lang/Object;", "GetGetValueHandler:Org.Apache.Commons.Lang3.Mutable.IMutableInvoker, MauiLibDemo")]
            get { return Value; }
            // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/interface[@name='Mutable']/method[@name='setValue' and count(parameter)=1 and parameter[1][@type='T']]"
            [Register ("setValue", "(Ljava/lang/Object;)V", "GetSetValue_Ljava_lang_Object_Handler:Org.Apache.Commons.Lang3.Mutable.IMutableInvoker, MauiLibDemo")]
            set { Value = global::Java.Interop.JavaObjectExtensions.JavaCast<global::Java.Lang.Number>(value)!; }
        }
}

Error CS0200 cannot assign a Value to the attribute or indexer "MutableShort.Value" - it is read-only

Hope wants!

public partial class MutableShort : global::Java.Lang.Number, global::Java.Lang.IComparable, global::Org.Apache.Commons.Lang3.Mutable.IMutable
        public virtual unsafe global::Java.Lang.Short? Value
        {
            // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/class[@name='MutableShort']/method[@name='getValue' and count(parameter)=0]"
            [Register("getValue", "()Ljava/lang/Short;", "GetGetValueHandler")]
            get
            {
                const string __id = "getValue.()Ljava/lang/Short;";
                try
                {
                    var __rm = _members.InstanceMethods.InvokeVirtualObjectMethod(__id, this, null);
                    return global::Java.Lang.Object.GetObject<global::Java.Lang.Short>(__rm.Handle, JniHandleOwnership.TransferLocalRef);
                }
                finally
                {
                }
            }
            set { }
        }

        // This method is explicitly implemented as a member of an instantiated Org.Apache.Commons.Lang3.Mutable.IMutable
        global::Java.Lang.Object? global::Org.Apache.Commons.Lang3.Mutable.IMutable.Value {
            // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/interface[@name='Mutable']/method[@name='getValue' and count(parameter)=0]"
            [Register ("getValue", "()Ljava/lang/Object;", "GetGetValueHandler:Org.Apache.Commons.Lang3.Mutable.IMutableInvoker, MauiLibDemo")]
            get { return Value; }
            // Metadata.xml XPath method reference: path="/api/package[@name='org.apache.commons.lang3.mutable']/interface[@name='Mutable']/method[@name='setValue' and count(parameter)=1 and parameter[1][@type='T']]"
            [Register ("setValue", "(Ljava/lang/Object;)V", "GetSetValue_Ljava_lang_Object_Handler:Org.Apache.Commons.Lang3.Mutable.IMutableInvoker, MauiLibDemo")]
            set { Value = (global::Java.Lang.Short?)global::Java.Interop.JavaObjectExtensions.JavaCast<global::Java.Lang.Number>(value)!; }
        }
}

How do I need to modify metadata.xml to meet expectations, or is there a good way to solve this problem.

jpobst commented 2 years ago

Can you post the Java you are trying to bind?

It looks like your MutableShort class does not have a setValue method.

SmallChi commented 2 years ago

java

package org.apache.commons.lang3.mutable;

public interface Mutable<T> {
  T getValue();

  void setValue(T paramT);
}

public class MutableShort extends Number implements Comparable<MutableShort>, Mutable<Number> {
  private static final long serialVersionUID = -2135791679L;

  private short value;

  public MutableShort() {}

  public MutableShort(short value) {
    this.value = value;
  }

  public MutableShort(Number value) {
    this.value = value.shortValue();
  }

  public MutableShort(String value) throws NumberFormatException {
    this.value = Short.parseShort(value);
  }

  public Short getValue() {
    return Short.valueOf(this.value);
  }

  public void setValue(short value) {
    this.value = value;
  }

  public void setValue(Number value) {
    this.value = value.shortValue();
  }

  public void increment() {
    this.value = (short)(this.value + 1);
  }

  public void decrement() {
    this.value = (short)(this.value - 1);
  }

  public void add(short operand) {
    this.value = (short)(this.value + operand);
  }

  public void add(Number operand) {
    this.value = (short)(this.value + operand.shortValue());
  }

  public void subtract(short operand) {
    this.value = (short)(this.value - operand);
  }

  public void subtract(Number operand) {
    this.value = (short)(this.value - operand.shortValue());
  }

  public short shortValue() {
    return this.value;
  }

  public int intValue() {
    return this.value;
  }

  public long longValue() {
    return this.value;
  }

  public float floatValue() {
    return this.value;
  }

  public double doubleValue() {
    return this.value;
  }

  public Short toShort() {
    return Short.valueOf(shortValue());
  }

  public boolean equals(Object obj) {
    if (obj instanceof MutableShort)
      return (this.value == ((MutableShort)obj).shortValue()); 
    return false;
  }

  public int hashCode() {
    return this.value;
  }

  public int compareTo(MutableShort other) {
    short anotherVal = other.value;
    return (this.value < anotherVal) ? -1 : ((this.value == anotherVal) ? 0 : 1);
  }

  public String toString() {
    return String.valueOf(this.value);
  }
}
jpobst commented 2 years ago

I wonder if the issue is that the getter is Short and the setter is short:

public Short getValue() {
  return Short.valueOf(this.value);
}

public void setValue(short value) {
  this.value = value;
}

I think these get treated differently since JNI thinks one is a short primitive type (S) and the other is an object type (Ljava/lang/Short;). If you can change the Java code so they match that would be my first suggestion.

If not, you can try changing them to match using metadata @managedType. I do not know if JNI will complain when we try to marshal an object instead of a short (or vice versa).

Alternatively you can use metadata @propertyName set to an empty string on all the getters/setters and they will not be converted to a property at all.

https://github.com/xamarin/java.interop/wiki/Troubleshooting-Android-Bindings-Issues

SmallChi commented 2 years ago

Thank you very much for the method you provided, since this is an aar package provided by a third party, viewed through the JD-JUI tool, so the Java code cannot be changed, so it can only be modified through the metadata configuration.😎