Tried to run a project in IDEA and got an exception. Before that project was always run on Eclipse only.
Here is the example code:
package com.test;
public class GenericMethod {
private Object value;
#SuppressWarnings("unchecked")
public <X> X getValue() {
return (X) value;
}
public void setValue(Object value) {
this.value = value;
}
}
And the class that uses it:
package com.test;
public class GenericMethodTest {
public static void main(String[] args) {
GenericMethod method = new GenericMethod();
method.setValue(3);
int[] array = new int[] {1, 2};
array[0] = method.getValue();
System.out.println(array[0]);
}
}
So if you try to run this example in Eclipse - everything works fine. It compiles and shows you correct result.
But if you try to run it, say, in IDEA or if you just use ant or your console and javac command - you'll get compile error:
Buildfile: C:\test\build.xml
build:
[javac] C:\test\build.xml:13: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
[javac] Compiling 1 source file to C:\test\bin
[javac] C:\test\src\com\test\GenericMethodTest.java:15: type parameters of <X>X cannot be determined; no unique maximal instance exists for type variable X with upper bounds int,java.lang.Object
[javac] array[0] = method.getValue();
[javac] ^
[javac] 1 error
What's happening?
Does Eclipse use it's own compiler?
Who has a bug?
In this case the code you posted has the bug. Eclipse let it slide (because you told it too with the #SuppressWarnings("unchecked")), but IDEA doesn't appear to be so forgiving. You can't cast value to X, because because of erasure you don't know what X is at runtime.
The proper way to do this would be to generify the class, and then store value as type X.
public class GenericMethod<X> {
private X value;
public X getValue() {
return value;
}
public void setValue(X value) {
this.value = value;
}
}
If you can't do that, then don't try to use generics here, just cast. Of course, if you were really really keen to use generics, you could also do something like this:
public <X> X getValue(Class<X> clazz) {
return clazz.cast(value);
}
But you'd have to have the class.
Related
I am trying to have proper assertion (with implicit null checks) for a property of a list element.
The first assertion is working as expected, except that it will generate no proper error message if actual is null.
The second is supposed to provide proper null check for actual, but it's not compiling.
Is there an option tweak the second assertion to make it work?
import java.util.List;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class ExampleTest {
private static class Sub {
private String value;
public String getValue() {
return value;
}
}
private static class Example {
private List<Sub> subs;
public List<Sub> getSubs() {
return subs;
}
}
#Test
void test() {
Example actual = null;
assertThat(actual.getSubs())//not null safe
.extracting(Sub::getValue)
.contains("something");
// assertThat(actual)
// .extracting(Example::getSubs)
// .extracting(Sub::getValue)//not compiling
// .contains("something");
}
}
For type-specific assertions, extracting(Function, InstanceOfAssertFactory) should be used:
assertThat(actual)
.extracting(Example::getSubs, as(list(Sub.class)))
.extracting(Sub::getValue) // compiles
.contains("something");
Assertions.as(InstanceOfAssertFactory) is an optional syntax sugar to improve readability
InstanceOfAssertFactories.list(Class) provides the list-specific assertions after the extracting call
I struggled to find a proper title for this question because the phenomenon I observed is very strange. Hence I skip explaining my problem literally and instead show you some (hopefully) self-describing code. Consider the following parameterized class:
public class GenericOptional<T> {
public GenericOptional(T someValue) {}
public T getValue() { return null; }
public Optional<String> getOptionalString() { return Optional.empty(); }
}
What I like to emphasize is that the return type Optional<String> of the method getOptionalString() does not depend on the type-parameter T.
Now have a look at the following code, which gets compiled inside Eclipse Luna 4.4.2 using Java 8u45:
public static void main(String[] args) {
Object obj = new GenericOptional<>(Boolean.TRUE);
GenericOptional go = (GenericOptional) obj;
Optional os = go.getOptionalString();
}
The local variable os has the type Optional without the type-parameter String! The Eclipse compiler has lost the information about the fixed type-parameter. Does anyone know why?
Now look at a second code example:
public static void main(String[] args) {
Object obj = new GenericOptional<>(Boolean.TRUE);
GenericOptional<?> go = (GenericOptional) obj;
Optional<String> os = go.getOptionalString();
}
By declaring the local variable go as GenericOptional<?> the return type of the method getOptionalString() now is Optional<String> as expected.
May anyone explain this behavior?
You are facing the behavior of raw types. When you are using a raw type, Generics are effectively turned off completely, regardless of whether there is a connection between the generic signature of the member and the type parameter of the class.
The reasoning behind this is that raw types are a feature for backward compatibility with pre-Generics code only. So either you have Generics or your don’t.
If the Generic method does not depend on the actual type parameter of the class, the problem is easy to fix:
GenericOptional<?> go = (GenericOptional<?>) obj;
Optional<String> os = go.getOptionalString();
Using <?> implies “I don’t know the actual type parameter and I don’t care but I’m using Generic type checking”.
It's not about Eclipse or anything, but about raw types.
Let's review this snippet:
public static void main(String[] args) {
Object obj = new GenericOptional<>(Boolean.TRUE);
GenericOptional go = (GenericOptional) obj;
Optional os = go.getOptionalString();
}
Here, you're creating a raw instance of GenericOptional, which means that the type-parameter information will be completely turned off. So, instantiating a raw GenericOptional means that the instance will expose the methods as following:
public class GenericOptional {
public GenericOptional(Object someValue) {}
public Object getValue() { return null; }
public Optional getOptionalString() { return Optional.empty(); }
}
However, if we now review the second snippet
public static void main(String[] args) {
Object obj = new GenericOptional<>(Boolean.TRUE);
GenericOptional<?> go = (GenericOptional) obj;
Optional<String> os = go.getOptionalString();
}
we can see that you're making a generic instance of GenericOptional. Even it's type-parameter is <?>, the compiler will not turn-off caring about type-parameters, so the instance will expose the getOptionalString() method parameterized, like this:
public Optional<String> getOptionalString() { return Optional.empty(); }
I'm trying to expand on the scenario asked in the SO question titled Ninject Factory Extension Bind Multiple Concrete Types To One Interface by using Ninject Conventions for convention-based binding of the ICar implementations.
I'm working off the accepted answer authored by Akim and his Gist outlining the full example.
The difference is that I've replaced the explicit ICar bindings with convention-based bindings (or an attempt at it, at least ;)
public class CarModule : NinjectModule
{
public override void Load()
{
Bind<ICarFactory>()
.ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());
// my unsuccessful binding
Kernel.Bind(scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ICar>()
.BindAllInterfaces());
//Bind<ICar>()
// .To<Mercedes>()
// .Named("Mercedes");
//Bind<ICar>()
// .To<Ferrari>()
// .Named("Ferrari");
}
}
When I attempt to instantiate the car variable in the test, I get an ActivationException:
Ninject.ActivationException was unhandled by user code
Message=Error activating ICar
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Request for ICar
Suggestions:
1) Ensure that you have defined a binding for ICar.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
Source=Ninject
StackTrace:
at Ninject.KernelBase.Resolve(IRequest request) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:line 362
at Ninject.ResolutionExtensions.GetResolutionIterator(IResolutionRoot root, Type service, Func`2 constraint, IEnumerable`1 parameters, Boolean isOptional, Boolean isUnique) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 263
at Ninject.ResolutionExtensions.Get(IResolutionRoot root, Type service, String name, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 164
at Ninject.Extensions.Factory.Factory.InstanceResolver.Get(Type type, String name, Func`2 constraint, ConstructorArgument[] constructorArguments, Boolean fallback) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\InstanceResolver.cs:line 75
at Ninject.Extensions.Factory.StandardInstanceProvider.GetInstance(IInstanceResolver instanceResolver, MethodInfo methodInfo, Object[] arguments) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\StandardInstanceProvider.cs:line 78
at Ninject.Extensions.Factory.FactoryInterceptor.Intercept(IInvocation invocation) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\FactoryInterceptor.cs:line 57
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.ICarFactoryProxy.CreateCar(String carType)
at Ninject.Extensions.Conventions.Tests.NinjectFactoryTests.A_Car_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryTests.cs:line 33
InnerException:
How can I get this test to pass?
[Fact]
public void A_Car_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument()
{
// auto-module loading is picking up my CarModule - otherwise, use:
// using (StandardKernel kernel = new StandardKernel(new CarModule()))
using (StandardKernel kernel = new StandardKernel())
{
// arrange
string carTypeArgument = "Mercedes";
ICarFactory factory = kernel.Get<ICarFactory>();
// act
var car = factory.CreateCar(carTypeArgument);
// assert
Assert.Equal(carTypeArgument, car.GetType().Name);
}
}
Here's the rest of the code, as condensed as possible, so that you don't have to refer to the original question
public interface ICarFactory { ICar CreateCar(string carType); }
public interface ICar { void Drive(); void Stop(); }
public class Mercedes : ICar {
public void Drive() { /* mercedes drives */ }
public void Stop() { /* mercedes stops */ }
}
public class Ferrari : ICar {
public void Drive() { /* ferrari drives */ }
public void Stop() { /* ferrari stops */ }
}
public class UseFirstArgumentAsNameInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments)
{
return (string) arguments[0];
}
protected override ConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments)
{
return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
}
}
Looks like, you have to define binding differently and provide your custom implementation of IBindingGenerator for this case
Binding
All implementation of ICar will have custom binding
Kernel.Bind(scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ICar>()
.BindWith(new BaseTypeBindingGenerator<ICar>()));
Custom IBindingGenerator implementation
Searching for all implementations of interface and bind them by type name
public class BaseTypeBindingGenerator<InterfaceType> : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type != null && !type.IsAbstract && type.IsClass && typeof(InterfaceType).IsAssignableFrom(type))
{
yield return bindingRoot.Bind(typeof(InterfaceType))
.To(type)
.Named(type.Name) as IBindingWhenInNamedWithOrOnSyntax<object>;
}
}
ps: here is a full sample
public class A{
private void javaMethod(int a,int b){}
private native void init()/*-{
function OnMouseMove(e) {
//blow calling doesn't work
this.#p::javaMethod(Ljava/...teger;Ljava.../Integer;)(intVal,intVal);
}
}-*/;
}
As described above,how to make that invoking work?
Answered on the Google Group: https://groups.google.com/d/msg/google-web-toolkit/qE2-L4u_t4s/YqjOu-bUfsAJ
Copied here for reference and convenience:
First, int is not java.lang.Integer, so your method signature in JSNI is wrong; it should read javaMethod(II).
(I suppose the #p:: while javaMethod is defined in class A is over-simplification in your question, but is OK in your code)
You'll also probably have a problem with this, that might not be what you think it is. A common pattern is to assign the current object (this, at the time) to a variable that you'll reference from your closure:
var that = this;
…
function OnMouseMove(e) {
that.#p.A::javaMethod(II)(intVal, intVal);
}
You're doing two things wrong:
You're not defining the class name after #p, (assuming #p is actually just a shortened version of the real package's name);
You're attempting to pass java.lang.Integer in place of int. You should be saying (II) as the types, as described here.
Your code should look more like this:
package com.my.package;
public class ClassA {
private static void javaMethod(int a, int b) { ... }
public static native void init() /*-{
$wnd.javaMethod = function(a, b) {
return #com.my.package.ClassA::javaMethod(II)(a,b);
}
function OnMouseMove(e) {
$wnd.javaMethod(a,b);
}
}-*/;
}
I tried the example from google at this page:
http://code.google.com/docreader/#p=google-web-toolkit-doc-1-5&s=google-web-toolkit-doc-1-5&t=DevGuideJavaFromJavaScript
I want to be able to call a Java method from JSNI, but nothing happens. No errors but the methods are not called. However, I can modify the fields from my class.
Here is the code I tried:
package com.jsni.client;
import com.google.gwt.core.client.EntryPoint;
public class Testjsnii implements EntryPoint {
String myInstanceField;
static int myStaticField;
void instanceFoo(String s) {
System.out.println(s);
}
static void staticFoo(String s) {
System.out.println(s);
}
public native void bar(Testjsnii x, String s) /*-{
this.#com.jsni.client.Testjsnii::instanceFoo(Ljava/lang/String;)(s);
x.#com.jsni.client.Testjsnii::instanceFoo(Ljava/lang/String;)(s);
#com.jsni.client.Testjsnii::staticFoo(Ljava/lang/String;)(s);
var val = this.#com.jsni.client.Testjsnii::myInstanceField;
}-*/;
public void onModuleLoad() {
bar(this,"Hello");
}
}
It prints nothing on the console but only a waring that says:
[WARN] [testjsnii] - JSNI method
'#com.jsni.client.Testjsnii::bar(Lcom/jsni/client/Testjsnii;Ljava/lang/String;)' returned > a value of type JavaScript object(1) but was declared void; it should not have returned a > value at all
I wonder what is the problem.
Thanks for the help.
You're actually running into a Chrome (10-dev) issue with the GWT DevMode plugin: http://code.google.com/p/google-web-toolkit/issues/detail?id=5778