MethodReferenceUsage

SUGGESTION

Style

View source code on GitHub

Summary

Prefer method references over lambda expressions

Suppression

Suppress false positives by adding the suppression annotation @SuppressWarnings("MethodReferenceUsage") to the enclosing element.

Disable this pattern completely by adding -Xep:MethodReferenceUsage:OFF as compiler argument. Learn more.

Samples

Replacement

Shows the difference in example code before and after the bug pattern is applied.

 import static java.util.Collections.emptyList;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.IntSupplier;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 class A {
   static class B extends A {
     final A a = new B();
     final B b = new B();
 
     IntSupplier intSup;
     Supplier<List<?>> listSup;
 
     void m() {
-      intSup = () -> a.iint0();
-      intSup = () -> b.iint0();
-      intSup = () -> this.iint0();
-      intSup = () -> super.iint0();
+      intSup = a::iint0;
+      intSup = b::iint0;
+      intSup = this::iint0;
+      intSup = super::iint0;
 
       intSup = () -> a.sint0();
       intSup = () -> b.sint0();
       intSup = () -> this.sint0();
       intSup = () -> super.sint0();
-      intSup = () -> A.sint0();
-      intSup = () -> B.sint0();
+      intSup = A::sint0;
+      intSup = B::sint0;
 
-      listSup = () -> Collections.emptyList();
-      listSup = () -> emptyList();
+      listSup = Collections::emptyList;
+      listSup = Collections::emptyList;
 
-      Stream.of((Class<?>) int.class).filter(c -> c.isEnum());
-      Stream.of((Map<?, ?>) null).map(Map::keySet).map(s -> s.size());
+      Stream.of((Class<?>) int.class).filter(Class::isEnum);
+      Stream.of((Map<?, ?>) null).map(Map::keySet).map(Set::size);
     }
 
     @Override
     int iint0() {
       return 0;
     }
   }
 
   int iint0() {
     return 0;
   }
 
   static int sint0() {
     return 0;
   }
 }

Identification

Shows code lines which will (not) be flagged by this bug pattern.
A //BUG: Diagnostic contains: comment is placed above any violating line.

import com.google.common.collect.Streams;
import java.util.HashMap;
import java.util.Map;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
import java.util.stream.Stream;

class A {
  private final Stream<Integer> s = Stream.of(1);
  private final Map<Integer, Integer> m = new HashMap<>();
  private final Runnable thrower =
      () -> {
        throw new RuntimeException();
      };

  void unaryExternalStaticFunctionCalls() {
    s.forEach(String::valueOf);
    // BUG: Diagnostic contains:
    s.forEach(v -> String.valueOf(v));
    s.forEach(
        // BUG: Diagnostic contains:
        (v) -> {
          String.valueOf(v);
        });
    s.forEach(
        // BUG: Diagnostic contains:
        (Integer v) -> {
          {
            String.valueOf(v);
          }
        });
    s.forEach(
        v -> {
          String.valueOf(v);
          String.valueOf(v);
        });

    s.map(String::valueOf);
    // BUG: Diagnostic contains:
    s.map(v -> String.valueOf(v));
    // BUG: Diagnostic contains:
    s.map((v) -> (String.valueOf(v)));
    s.map(
        // BUG: Diagnostic contains:
        (Integer v) -> {
          return String.valueOf(v);
        });
    s.map(
        // BUG: Diagnostic contains:
        (final Integer v) -> {
          return (String.valueOf(v));
        });
    s.map(
        v -> {
          String.valueOf(v);
          return String.valueOf(v);
        });

    s.findFirst().orElseGet(() -> Integer.valueOf("0"));
    m.forEach((k, v) -> String.valueOf(v));
    m.forEach((k, v) -> String.valueOf(k));
  }

  void binaryExternalInstanceFunctionCalls() {
    m.forEach(m::put);
    // BUG: Diagnostic contains:
    m.forEach((k, v) -> m.put(k, v));
    m.forEach((k, v) -> m.put(v, k));
    m.forEach(
        // BUG: Diagnostic contains:
        (Integer k, Integer v) -> {
          m.put(k, v);
        });
    m.forEach(
        (k, v) -> {
          m.put(k, k);
        });
    m.forEach(
        // BUG: Diagnostic contains:
        (final Integer k, final Integer v) -> {
          {
            m.put(k, v);
          }
        });
    m.forEach(
        (k, v) -> {
          {
            m.put(v, v);
          }
        });
    m.forEach((k, v) -> new HashMap<Integer, Integer>().put(k, v));
    m.forEach(
        (k, v) -> {
          m.put(k, v);
          m.put(k, v);
        });

    Streams.zip(s, s, m::put);
    // BUG: Diagnostic contains:
    Streams.zip(s, s, (a, b) -> m.put(a, b));
    Streams.zip(s, s, (a, b) -> m.put(b, a));
    // BUG: Diagnostic contains:
    Streams.zip(s, s, (Integer a, Integer b) -> (m.put(a, b)));
    Streams.zip(s, s, (a, b) -> (m.put(a, a)));
    Streams.zip(
        s,
        s,
        // BUG: Diagnostic contains:
        (final Integer a, final Integer b) -> {
          return m.put(a, b);
        });
    Streams.zip(
        s,
        s,
        (a, b) -> {
          return m.put(b, b);
        });
    Streams.zip(
        s,
        s,
        // BUG: Diagnostic contains:
        (a, b) -> {
          return (m.put(a, b));
        });
    Streams.zip(
        s,
        s,
        (a, b) -> {
          return (m.put(b, a));
        });
    Streams.zip(
        s,
        s,
        (a, b) -> {
          m.put(a, b);
          return m.put(a, b);
        });
  }

  void nullaryExternalInstanceFunctionCalls() {
    s.map(Integer::doubleValue);
    // BUG: Diagnostic contains:
    s.map(i -> i.doubleValue());
    s.map(i -> i.toString());
    s.map(i -> s.toString());

    // BUG: Diagnostic contains:
    Stream.of(int.class).filter(c -> c.isEnum());
    Stream.of((Class<?>) int.class).filter(Class::isEnum);
    // BUG: Diagnostic contains:
    Stream.of((Class<?>) int.class).filter(c -> c.isEnum());
  }

  void localFunctionCalls() {
    s.forEach(v -> ivoid0());
    s.forEach(v -> iint0());
    s.forEach(v -> svoid0());
    s.forEach(v -> sint0());

    s.forEach(this::ivoid1);
    // BUG: Diagnostic contains:
    s.forEach(v -> ivoid1(v));
    s.forEach(
        // BUG: Diagnostic contains:
        v -> {
          ivoid1(v);
        });
    s.forEach(this::iint1);
    // BUG: Diagnostic contains:
    s.forEach(v -> iint1(v));
    s.forEach(
        // BUG: Diagnostic contains:
        v -> {
          iint1(v);
        });

    s.forEach(A::svoid1);
    // BUG: Diagnostic contains:
    s.forEach(v -> svoid1(v));
    s.forEach(
        // BUG: Diagnostic contains:
        v -> {
          svoid1(v);
        });
    s.forEach(A::sint1);
    // BUG: Diagnostic contains:
    s.forEach(v -> sint1(v));
    s.forEach(
        // BUG: Diagnostic contains:
        v -> {
          sint1(v);
        });

    s.forEach(v -> ivoid2(v, v));
    s.forEach(v -> iint2(v, v));
    s.forEach(v -> svoid2(v, v));
    s.forEach(v -> sint2(v, v));

    m.forEach((k, v) -> ivoid0());
    m.forEach((k, v) -> iint0());
    m.forEach((k, v) -> svoid0());
    m.forEach((k, v) -> sint0());

    m.forEach(this::ivoid2);
    // BUG: Diagnostic contains:
    m.forEach((k, v) -> ivoid2(k, v));
    m.forEach(
        // BUG: Diagnostic contains:
        (k, v) -> {
          ivoid2(k, v);
        });
    m.forEach(this::iint2);
    // BUG: Diagnostic contains:
    m.forEach((k, v) -> iint2(k, v));
    m.forEach(
        // BUG: Diagnostic contains:
        (k, v) -> {
          iint2(k, v);
        });

    m.forEach(A::svoid2);
    // BUG: Diagnostic contains:
    m.forEach((k, v) -> svoid2(k, v));
    m.forEach(
        // BUG: Diagnostic contains:
        (k, v) -> {
          svoid2(k, v);
        });
    m.forEach(A::sint2);
    // BUG: Diagnostic contains:
    m.forEach((k, v) -> sint2(k, v));
    m.forEach(
        // BUG: Diagnostic contains:
        (k, v) -> {
          sint2(k, v);
        });
  }

  void functionCallsWhoseReplacementWouldBeAmbiguous() {
    receiver(
        i -> {
          Integer.toString(i);
        });
  }

  void assortedOtherEdgeCases() {
    s.forEach(v -> String.valueOf(v.toString()));
    TernaryOp o1 = (a, b, c) -> String.valueOf(a);
    TernaryOp o2 = (a, b, c) -> String.valueOf(b);
    TernaryOp o3 = (a, b, c) -> String.valueOf(c);
    TernaryOp o4 = (a, b, c) -> c.concat(a);
    TernaryOp o5 = (a, b, c) -> c.concat(b);
    TernaryOp o6 = (a, b, c) -> a.concat(c);
    TernaryOp o7 = (a, b, c) -> b.concat(c);
  }

  void receiver(IntFunction<?> op) {}

  void receiver(IntConsumer op) {}

  void ivoid0() {}

  void ivoid1(int a) {}

  void ivoid2(int a, int b) {}

  int iint0() {
    return 0;
  }

  int iint1(int a) {
    return 0;
  }

  int iint2(int a, int b) {
    return 0;
  }

  static void svoid0() {}

  static void svoid1(int a) {}

  static void svoid2(int a, int b) {}

  static void svoid3(int a, int b, int c) {}

  static int sint0() {
    return 0;
  }

  static int sint1(int a) {
    return 0;
  }

  static int sint2(int a, int b) {
    return 0;
  }

  interface TernaryOp {
    String collect(String a, String b, String c);
  }
}

Copyright © 2017-2023 Picnic Technologies BV