Improve Intellij Code Inspection for may produce NullPointerException warnings - java

I have a class with a hasField function that checks if a field is present and not null, and a getField function that returns the value of the field (or null if not present).
In my code when I call getField right after checking hasField, I know that getField is not going to return null, but the IDE Inspection (Constant Conditions and Exceptions) doesn't know that. I get a bunch of method method name may produce a NullPointerException
I'm trying to find a clean way to make this warning go away.
Workarounds
Here are some workarounds I could do but I find all of these hacky:
Surround getField with Objects.requireNotnull, the code would be no-op. Would prefer not doing that as it makes the code slightly less readable.
Suppress warnings where I know this is safe. Again not preferred as this is going to happen at a bunch of places in our code.
Ignore warnings. In this case we might miss legit warnings just because warnings section will be too noisy.
Ideal solution
Would I be able to somehow set up the warnings in such a way that if hasField is true, then getField will return a non-null? I looked into JetBrains Contract Annotations but doing what I want here seems to be beyond what is supported with #Contract
Code Sample
Here's a minimum working code sample that demonstrates the issue:
import javax.annotation.Nullable;
public class Hello {
private Hello(){}
public static void main(String[] args) {
TestClass test1 = new TestClass(null);
if (test1.hasSample()) {
System.out.println(test1.getSample().equals("abc"));
}
}
}
class TestClass {
private final String sample;
TestClass(String field) { this.sample = field; }
boolean hasSample() { return sample != null; }
#Nullable public String getSample() { return sample; }
}
I get the following warning
Method invocation equals may produce NullPointerException
I'd ideally want to be able to tell IDE that getSample is not null when hasSample is true.

Disclosure I'm IntelliJ IDEA developer responsible for this subsystem
No, it's not possible now. There's no better solution than possible workarounds you already listed, assuming that you cannot change the API. The closest thing we have is the inlining of very trivial methods. However, it works only if:
The methods like hasSample() and getSample() are called from the same class
The called methods cannot be overridden (private/static/final/declared in final class)
E.g. this feature works in the following code:
final class TestClass { // if final is removed, the warning will appear again
private final String sample;
TestClass(String field) { this.sample = field; }
boolean hasSample() { return sample != null; }
#Nullable
public String getSample() { return sample; }
#Override
public String toString() {
if (hasSample()) {
return "TestClass: "+getSample().trim(); // no warning on trim() invocation here
}
return "TestClass";
}
}
For now, I can only suggest refactoring your APIs to Optionals like this:
import java.util.Optional;
public class Hello {
private Hello(){}
public static void main(String[] args) {
TestClass test1 = new TestClass(null);
test1.getSample().ifPresent(s -> System.out.println(s.equals("abc")));
// or fancier: test1.getSample().map("abc"::equals).ifPresent(System.out::println);
}
}
final class TestClass {
private final String sample;
TestClass(String field) { this.sample = field; }
public Optional<String> getSample() { return Optional.ofNullable(sample); }
}

Related

Reference to NamedParameterJdbcTemplate is ambiguous, both batchUpdate(String,Map<String,?>[]) and batchUpdate(String,SqlParameterSource[]) match

Maybe I'm missing something. I get the following error when I try to compile code that is essentially the following:
namedParameterJdbcTemplate.batchUpdate(SQL,
values.stream().map(val->new MapBuilder<String,Object>()
.put("param1",val.getSomeProperty())
.put("param2",val.getSomeOtherProperty())
.build())
.toArray(Map[]::new);
Somehow this is ambiguous between batchUpdate(String,Map[]) and batchUpdate(String,SqlParameterSource[]).
SqlParamterSource does not implement Map. So where is the confusion? Even IntelliJ's incremental compiler doesn't seem to have a problem with the code. It's only when I attempt to compile the code from maven, or to run the code that it flags it as an error.
I can work around the issue by changing the code as follows:
namedParameterJdbcTemplate.batchUpdate(SQL,
values.stream().map(val->new MapBuilder<String,Object>()
.put("param1",val.getSomeProperty())
.put("param2",val.getSomeOtherProperty())
.build())
.collect(Collectors.toList())
.toArray(new Map[0]);
I've tried searching for an answer, but all of the hits just explain scenarios where the ambiguity is already clear (to me). This scenario however is puzzling to me.
Does anyone have any insight?
Thanks,
Dave
Not exactly an answer
After some investigation, the compile error is due to :
Overloading of method.
Calling overloading method where parameter is passed directly from generic method where the return type is inferred by a Lambda Expression or Double Column Operator.
However the reason (related JLS) is not found yet. Hope someone can give more explanation.
Demonstration of the problem
The following code snippet demonstrates the above founding. The code is tested with jdk 1.8.0_151. Interestingly eclipse does not give compile error.
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntFunction;
public class AmbitguousMethodTest {
private <A> A inferByFunctionalInterface(IntFunction<A> d) {
return null;
}
private <A> A inferByArray(A[] a) {
return null;
}
private <A> A inferByOutside() {
return null;
}
private <A> A inferByList(List<A> d) {
return null;
}
private void overloadMethod(BigDecimal a) {
}
private void overloadMethod(Integer a) {
}
public static void main(String[] args) {
AmbitguousMethodTest test = new AmbitguousMethodTest();
testOverloading(test);
}
private static void testOverloading(AmbitguousMethodTest test) {
// Compilation failure reference to overloadMethod is ambiguous
// By double colon operator
test.overloadMethod(test.inferByFunctionalInterface(BigDecimal::new));
// By Lambda expression
test.overloadMethod(test.inferByFunctionalInterface(i->new BigDecimal(i)));
// Compile
// By explicit functional interface
test.overloadMethod(test.inferByFunctionalInterface(new IntFunction<BigDecimal>() {
#Override
public BigDecimal apply(int value) {
return new BigDecimal(1);
}
}));
test.overloadMethod(test.inferByArray(new BigDecimal[0]));
test.overloadMethod(test.<BigDecimal>inferByOutside());
test.overloadMethod(test.inferByList(new ArrayList<BigDecimal>()));
}
}

Using power mockito to suppress private static method

I have made a small example of my code to illustrate the problem
public class SiteTranslator {
Integer id;
//Other fields
}
public class SiteUtil {
private static SiteTranslator siteTranslator = getSiteTranslator();
private static SiteTranslator getSiteTranslator()
{
SiteTranslator siteTranslator;
//Logic involving network call
return siteTranslator;
}
private static String getEnvironment()
{
String env = "";
//Logic
return env;
}
public static int getParent(int siteId)
{
int parentId = 0;
//Logic using siteTranslator from getSiteTranslator()
return parentId;
}
}
public class SiteUtilTest {
#Test
public void test1()
{
try
{
PowerMockito.suppress(SiteUtil.class.getMethod("getSiteTranslator"));
BDDMockito.given(SiteUtil.getParent(1)).willReturn(6);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
The SiteTranslator object we get from getSiteTranslator() method is used by my public function getParent(). Since getSiteTranslator() requires a network call , it needs to be suppressed. I however get the following error
java.lang.NoSuchMethodException: SiteUtil.getSiteTranslator()
I believe the problem is because I'm trying to mock a private static function. However I cannot change it to public. Is there a way to mock the code in its current state.
In fact, you don't need Powermockito to achieve what you need.
At the moment, you think you need Powermockito to suppress a private static method but this is definitely not the way to go.
Instead you should refactor your code to make it easier to test:
Remove static qualifiers
Use dependency injection
After such a refactor, you end up with something like that (no mocking needed !):
public class SiteUtil {
private SiteTranslator siteTranslator;
public SiteUtil(SiteTranslator siteTranslator) {
this.siteTranslator = siteTranslator;
}
public int getParent(int siteId) {
int parentId = 0;
// Logic using siteTranslator
return parentId;
}
...
}
Now you can test it like that:
public class SiteUtilSpec {
private final SiteTranslator defaultTranslator = new DummySiteTranslator();
#Test
public void itShouldReturnTheSixthSiteWhenWeProvideTheFirstParent() {
SiteUtil site = new SiteUtil(defaultTranslator);
int parentId = site.getParent(1);
assertEquals(6, parentId);
}
}
DummySiteTranslator is a fake object (maybe it is embedding a bunch of hardcoded translations useful for testing) but the point is that this object never do any network call ! Making its usage safe and fast (ideal for testing).
The answer by "Spotted" already nails it, as the core problem is: you created hard-to-test code for absolutely no reason.
Using such internal static calls simply makes your program hard to test; and surprise: it also makes it hard to maintain, enhance, reuse. The fact that you need to turn to Powermock is very often simply an indication that your production code is bad. Now you can choose between using PowerMock to "fix" that problem; or to really fix the problem, by changing your production code - it is simply bad practice to solve problems the way your example code does!
So, the other real lesson here is: you want to spend some time to learn how to write code that does not have such problems; for example by watching those videos.

Can MethodHandle be used by frameworks/libraries (instead of traditional reflection)?

In my framework I have a class like this:
public class Foo<B, V> {
private final Method getterMethod;
...
public V executeGetter(B bean) {
try {
return getterMethod.invoke(bean);
} catch ...
}
}
This class is used to call getters of classes created by users that aren't available at compile time of my framework. For example, B might be a class called Person.
Through profiling, I've discovered that this method is horribly slow. The Method.invoke() takes 40% of performance in sampling profiling (even with setAccessible(true)), while a non reflective implementation takes only a small fraction of that performance.
So I 'd like to replace is with a MethodHandle:
public class Foo<B, V> {
private final MethodHandle getterMethodHandle;
...
public V executeGetter(B bean) {
try {
return getterMethodHandle.invoke(bean);
} catch ...
}
}
But then I get this exception:
java.lang.ClassCastException: Cannot cast [Ljava.lang.Object; to Person
at sun.invoke.util.ValueConversions.newClassCastException(ValueConversions.java:461)
at sun.invoke.util.ValueConversions.castReference(ValueConversions.java:456)
at ...Foo.executeGetter(Foo.java:123)
even though bean is an instance of Person. Now the misleading part is that it's trying to cast an Object[] (and not an Object) to Person. Note that wrapping it in an object array (which is a performance loss) doesn't help:
return getterMethodHandle.invoke(new Object[]{bean}); // Same exception
Is it possible to get the MethodHandle to work in this situation?
That ClassCastException only occurs if you compile with source/target level java 6.
Compile with source/target level 7 or higher to avoid that ClassCastException.
Answer found thanks to Tagir's answer. (vote up his answer too)
Using MethodHandles in framework/library code is perfectly fine and I see no problem in your code. This example works just fine:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class Foo<B, V> {
private final MethodHandle getterMethodHandle;
public Foo(MethodHandle mh) {
this.getterMethodHandle = mh;
}
public V executeGetter(B bean) {
try {
return (V) getterMethodHandle.invoke(bean);
} catch(RuntimeException | Error ex) {
throw ex;
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
static class Pojo {
String x;
public Pojo(String x) {
this.x = x;
}
public String getX() {
return x;
}
}
public static void main(String[] args) throws Exception {
// I prefer explicit use of findXYZ
Foo<Pojo, String> foo = new Foo<>(MethodHandles.lookup()
.findVirtual(Pojo.class, "getX", MethodType.methodType(String.class)));
// Though unreflect also works fine
Foo<Pojo, String> foo2 = new Foo<>(MethodHandles.lookup()
.unreflect(Pojo.class.getMethod("getX")));
System.out.println(foo.executeGetter(new Pojo("foo")));
System.out.println(foo2.executeGetter(new Pojo("bar")));
}
}
The output is:
foo
bar
For even better performance consider using invokeExact, though it will not allow you automatic type conversions like unboxing.

Expression that behaves differently inside a static method

I'm trying to write an expression or series of statements of Java source code that when written inside a static method evaluates to null, but if the method is non-static evaluates to this.
My initial idea was to 'overload' on static vs non-static, as below:
public class test {
public void method1() {
System.out.println(getThisOrNull());
}
public static void method2() {
System.out.println(getThisOrNull());
}
private static Object getThisOrNull() {
return null;
}
private Object getThisOrNull() {
return this;
}
public static void main(String[] args) {
test t = new test();
System.out.println(t);
t.method1();
t.method2();
}
}
Unfortunately this isn't actually legal Java, you can't 'overload' like that and it just gives a compiler error:
test.java:14: error: method getThisOrNull() is already defined in class test
private Object getThisOrNull() {
^
1 error
Clearly in an ideal world I wouldn't write it like that to begin with, but the problem is this code will be generated automatically by a tool that is not really semantically or syntactically enough to distinguish between the static vs non-static case.
So, how can I write some source code that, although byte for byte identical compiles and behaves differently in depending on the presence of the static modifier for the method?
This can be achieved with a trick and a bit of help from Java's reflection facilities. It's ugly, but it works:
import java.lang.reflect.Field;
public class test {
public void method1() {
System.out.println(getThisOrNull(new Object(){}));
}
public static void method2() {
System.out.println(getThisOrNull(new Object(){}));
}
private static Object getThisOrNull(final Object o) {
for (Field f: o.getClass().getDeclaredFields()) {
if (f.getType().equals(test.class)) {
try {
return f.get(o);
}
catch (IllegalAccessException e) {
// Omm nom nom...
}
}
}
return null;
}
public static void main(String[] args) {
test t = new test();
System.out.println(t);
t.method1();
t.method2();
}
}
This compiles and runs as hoped for:
test#183f74d
test#183f74d
null
The trick that makes this possible is the use of new Object(){}, which creates a new, anonymous class within the existing method that we're trying to figure out if it's static or not. The behaviour of this is subtly different between the two cases.
If the goal were just to figure out if the method is static or not we could write:
java.lang.reflect.Modifiers.isStatic(new Object(){}.getClass().getEnclosingMethod().getModifiers())
Since we want to get this (when available) we need to do something slightly different. Fortunately for us classes defined within the context of an instance of an object in Java get an implicit reference to the class that contains them. (Normally you'd access it with test.this syntax). We needed a way to access test.this if it existed, except we can't actually write test.this anywhere because it too would be syntactically invalid in the static case. It does however exist within the object, as a private member variable. This means that we can find it with reflection, which is what the getThisOrNull static method does with the local anonymous type.
The downside is that we create an anonymous class in every method we use this trick and it probably adds overheads, but if you're backed into a corner and looking for a way of doing this it does at least work.

ProGuard removing method call creating useless code

So I have small interface
public interface IPlayersStorage
{
// other methods...
public boolean addException(final String nick);
// other methods...
}
and class "PlayersStorage" that implements it: (only used part)
public class PlayersStorage implements IPlayersStorage
{
private static final PlayersStorage inst = new PlayersStorage();
private final Set<String> exceptions = new HashSet<>(50);
#Override
public boolean addException(final String nick)
{
return ! this.exceptions.add(nick);
}
public static PlayersStorage getStorage()
{
return inst;
}
}
And in some place I use that method using that code:
for (final String player : this.cfg.getStringList("Exceptions"))
{
PlayersStorage.getStorage().addException(player);
}
And ProGuard change it to:
for (Iterator localIterator1 = this.cfg.getStringList("Exceptions").iterator(); localIterator1.hasNext();)
{
localIterator1.next();
PlayersStorage.getStorage(); // it's get object, but don't do anything with it...
}
The only possible fix that I found, is add static method to PlayersStorage
public static boolean staticAddException(final String nick)
{
return inst.addException(nick);
}
And then use it (instead of old code)
for (final String player : this.cfg.getStringList("Exceptions"))
{
PlayersStorage.staticAddException(player);
}
Then works... (ProGuard keep method call) but adding static methods for every method from interface isn't good idea.
ProGuard only removes method invocations if they don't have any effect (doesn't seem to be the case here), or if you have specified -assumenosideffects for the methods. You should check your configuration and remove any such option.
Alternatively, your decompiler may be having problems decompiling the code. You should then check the actual bytecode with javap -c.

Categories

Resources