Home > front end >  Method that takes any setter as a parameter
Method that takes any setter as a parameter

Time:05-30

I have an Item class

public class Item {
    Long id;
    String name;
    Boolean isGood;

// getters and setters
}

And I want to declare a method that can take a method reference and some value as arguments.

What I tried

public class Main {
    public static void main(String[] args) {
        Item item = new Item();
        
        setLong(item::setId, 123L);
        setBool(item::setGood, true);
        setString(item::setName, "Foo Bar");

   }

   static void setLong(Consumer<Long> setter, Long o) {
        setter.accept(o);
    }

    static void setBool(Consumer<Boolean> setter, Boolean o) {
        setter.accept(o);
    } 
    
    static void setString(Consumer<String> setter, String o) {
        setter.accept(o);
    }
}

But I want something like

set(Long.class, item::setId, 123L);
set(Boolean.class, item::setGood, true);
set(String.class, item::setName, "Foo Bar");

How can I do that?

CodePudding user response:

Try this.

import java.util.function.Consumer;

public class SOQ_20220528
{

   public static class Example
   {
   
      private Short a;
      private Integer b;
      private Long c;
   
      public Example() {}
   
      public Short getA() {
         return this.a;}
      public Integer getB() {
         return this.b;}
      public Long getC() {
         return this.c;}
   
      public void setA(Short a) {this.a = a;}
      public void setB(Integer b) {this.b = b;}
      public void setC(Long c) {this.c = c;}
      
      public String toString() { 
         return this.a   " "   this.b   " "   this.c; }
   
   }
   
   public static void main(String[] args)
   {
   
      final Example instanceName= new Example();
      
      final Short tuv = 1;
   
      set(Short.class, instanceName::setA, Short.valueOf(tuv));
      set(Integer.class, instanceName::setB, Integer.valueOf(2));
      set(Long.class, instanceName::setC, Long.valueOf(3));
   
   }
   
   public static <T> void set(Class<T> clazz, Consumer<T> setter, T value)
   {
   
      setter.accept(value);
   
   }

}

We place the Class of the type we want to use as the first parameter, then we put the setter as our second parameter, and finally, we put the literal value we want our object to adapt as our third parameter.

The first parameter is necessary because of type erasure at runtime. Java simply doesn't keep the type at runtime, so we just need to pass in the Class, which functions as our runtime type.

The second parameter is necessary, as you need a setter to perform. However, notice that I did instanceName::setA instead of Example::setA. Since this is the section of the code where we are providing the consumer to use, I am using the instance name instead of the type name. There are many times where it will actually be the other way around (one example is with static methods).

The third parameter is the actual value that will be the parameter of the setter. I am setting field a of instanceName to be the Short value 1. That 1 portion is the third parameter I am talking about.

EDIT - turns out that for this particular example, the Class<T> parameter is not necessary. I will leave it in for simplicity of modification, but yes, you could technically have a method header like this instead -- public static <T> void set(Consumer<T> setter, T value), then you would supply the parameters to this method the same as before, just without the class.

  • Related