java.interop icon indicating copy to clipboard operation
java.interop copied to clipboard

Use Java generic type parameters constraints

Open jonpryor opened this issue 5 years ago • 3 comments

generator doesn't "forward" and use generic type constraints in bindings. Consider this Java type:

package com.xamarin.android;

public interface InvokeRunnable<T extends Runnable> {
    void invoke (T runnable);
}

Here, T is contained to be a type that implements java.lang.Runnable.

However, the binding that generator emits is:

	[Register ("com/xamarin/android/InvokeRunnable", "", "Com.Xamarin.Android.IInvokeRunnableInvoker")]
	[global::Java.Interop.JavaTypeParameters (new string [] {"T extends java.lang.Runnable"})]
	public partial interface IInvokeRunnable : IJavaObject, IJavaPeerable {

		// Metadata.xml XPath method reference: path="/api/package[@name='com.xamarin.android']/interface[@name='InvokeRunnable']/method[@name='invoke' and count(parameter)=1 and parameter[1][@type='T']]"
		[Register ("invoke", "(Ljava/lang/Runnable;)V", "GetInvoke_Ljava_lang_Runnable_Handler:Com.Xamarin.Android.IInvokeRunnableInvoker, Xamarin.Android.McwGen-Tests")]
		void Invoke (global::Java.Lang.Object p0);

	}

The Java-side parameter, which must be a java.lang.Runnable or type implementing that interface, is bound as Java.Lang.Object.

Feature request: "forward" this constraint information, and use it in the binding, so that we instead emit:

	public partial interface IInvokeRunnable : IJavaObject, IJavaPeerable {

		[Register ("invoke", "(Ljava/lang/Runnable;)V", "GetInvoke_Ljava_lang_Runnable_Handler:Com.Xamarin.Android.IInvokeRunnableInvoker, Xamarin.Android.McwGen-Tests")]
		void Invoke (global::Java.Lang.IRunnable p0);
	}

jonpryor avatar Jun 19 '20 20:06 jonpryor

While trying to fix issues in a Java library I'm binding I came upon this issue. Easily fixable with Metadata attr BUT I'm curious as to why the C# interface isn't a real generic interface like

public partial interface IInvokeRunnable <T> : IJavaObject, IJavaPeerable
where T : global::Java.Lang.IRunnable
{
    [Register ("invoke", "(Ljava/lang/Runnable;)V", "GetInvoke_Ljava_lang_Runnable_Handler:Com.Xamarin.Android.IInvokeRunnableInvoker, Xamarin.Android.McwGen-Tests")]
    void Invoke (T p0);
}

I know there is a reason I'm completely overlooking because I've seen this "issue" in classes too.

AmrAlSayed0 avatar Feb 06 '21 23:02 AmrAlSayed0

Unlike .NET, Java generics do not exist in compiled code or at runtime. They are simply compiler magic that the compiler removes when compiling code.

Since we bind .jar files, the generic information is gone.

Assuming that could be fixed, we have another issue at runtime. If Java gives us an arbitrary ArrayList object and we want to convert it to a ArrayList<T>, we don't know what T is.

For these reasons, our bindings reflect the Java compiled API (without generics) and not the source API.

jpobst avatar Feb 08 '21 16:02 jpobst

But generics with constraints is fine cuz you know what T is, got it. I suspected that would be the reason.

I really want to see this implemented (+ covariant returns) because I have seen Builder<T extends Builder<T>> classes basically become useless when all of the return types of all the methods become Java.Lang.Object when it's clearly more than that.

AmrAlSayed0 avatar Feb 08 '21 17:02 AmrAlSayed0