0

I have the following code that i want to put into a method to be reused:

    List<ClText150> names = attribute.getName().getClText150();
    for (ClText150 clText : names) {
        Locale locale = Locale.forLanguageTag(clText.getCl());
        AttributeText attributeText = textsMap.get(locale);
        if (attributeText == null) {
            attributeText = createText(locale);
            textsMap.put(locale, attributeText);
        }
        attributeText.setName(clText.getValue());
    }

This i need for different lists of ClText150 (an object from a xml). Same code i need for

List<ClText150> shortNames = attribute.getShortname().getClText150();

But this time caling setShortname with the value. The same i need for other fields. Is there a way to parametrize this setter?

I created a method doing the job:

private void handle150(List<ClText150> list, Consumer<String> setter) {
    for (ClText150 clText : list) {
        Locale locale = Locale.forLanguageTag(clText.getCl());
        AttributeText attributeText = textsMap.get(locale);
        if (attributeText == null) {
            attributeText = createText(locale);
            
        }
        setter.accept(clText.getValue());
        //attributeText.setName(clText.getValue());
    }
}

The problem is how to call this method declaring the setter of the object (attributeText) that exists only in the method. This is obviously not working: handle150(names, AttributeText::setName);

If not possible what alternatives i have to just copy paste the code?

dermoritz
  • 12,519
  • 25
  • 97
  • 185

2 Answers2

2

I found an answer. The setter must be declared as a BiConsumer not a consumer see Lambda expression for setter

So in my case:

private void handle150(List<ClText150> list, BiConsumer<AttributeText, String> setter) {
    for (ClText150 clText : list) {
        Locale locale = Locale.forLanguageTag(clText.getCl());
        AttributeText attributeText = textsMap.get(locale);
        if (attributeText == null) {
            attributeText = createText(locale);
            
        }
        setter.accept(attributeText, clText.getValue());
    }
}

With this i can call with static method reference:

        handle150(names, AttributeText::setName);
        handle150(descriptions, AttributeText::setDescription);
        handle150(longNames, AttributeText::setLongName);
        handle150(shortNames, AttributeText::setShortName);

why and how this works is explained here.

dermoritz
  • 12,519
  • 25
  • 97
  • 185
1

This is based on my understanding. I am trying to simplify this. However if you feel this does answer your question, please tell me and I will remove it.

   enum ConsumeOptions{
        NAME, SHORTNAME
    }

 

    private void handleAttributeText(ConsumeOptions option, AttributeText attributeText, String value){
    
           switch (option) {
                case NAME: 
                         attributeText.setName(value);
                         break;
                case SHORTNAME:
                         attributeText.setShortName(value);
                         break;
    }

private void handle150(List<ClText150> list, ConsumeOptions option) {

    for (ClText150 clText : list) {

        if (attributeText == null) {
            attributeText = createText(locale);
            
        }
           handleAttributeText(ConsumeOptions.NAME, attributeText, "newValue");
    }
}
dermoritz
  • 12,519
  • 25
  • 97
  • 185
JCompetence
  • 6,997
  • 3
  • 19
  • 26
  • thanks - this might indeed work and it is straight forward. But you brought-up reflection and this works also. And in my case using reflection i can handle also "ClText2000" and all other declared in xml (same methods but different classes.) I'll wait a bit but most likely also accept your answer – dermoritz Aug 10 '21 at 13:58
  • I wont persuade you otherwise. Just remember to keep it simple for future developers who will maintain your code. – JCompetence Aug 10 '21 at 13:59