0

C macros would solve my problem very easily... but it appears that Java requires code to achieve the effect of declaring sub-arrays that are statically assembled by the compiler. The intent is to be able to define a global array / list of Strings that can be reused across a large number of classes.

C construct using (evil) macros

global.c source file

#define GLOBAL_HTML_ATTRIBUTES "accesskey", "class", "contenteditable", "contextmenu", "dir", "draggable", "dropzone", "hidden", "id", "lang", "spellcheck", "style", "tabindex", "title", "translate"

a.c source file

#define A_ELEMENT_ATTRIBUTES "download", "href", "hreflang", "media", "name", "rel", "target", "type"

char[] attributes = {GLOBAL_HTML_ATTRIBUTES, A_ELEMENT_ATTRIBUTES};

Is there a similar Java construct?

And yes, I'm aware of the other questions posted that ask about concatenating arrays of Strings ... this question is intended to help a long time C developer acclimate to Java programming idioms.

Neoheurist
  • 3,183
  • 6
  • 37
  • 55
  • 1
    Concatenating string arrays:http://stackoverflow.com/questions/3750500/how-do-i-concatenate-static-string-arrays – Neoheurist May 29 '15 at 21:18
  • Concatenating two string arrays: http://stackoverflow.com/questions/80476/how-to-concatenate-two-arrays-in-java – Neoheurist May 29 '15 at 21:19
  • 5
    Why are you asking this, and posting comments that clearly answer your question? – austin wernli May 29 '15 at 21:20
  • 3
    No, there's no shorthand way of doing this -- but concatenating the arrays at runtime isn't going to be a performance issue; it's a tiny one-time startup cost. – Louis Wasserman May 29 '15 at 21:21
  • 2
    *"I'm aware of the other questions"*... so the question is whether there is something like macros in Java. And the answer is: **no, there is not**. You really need to go for the runtime concatenation approach. – Pavel Horal May 29 '15 at 21:21
  • Because I'm a C developer who does not know the full range of best practices that Java has to offer... it seems that there might well be an option that was not explored in the answers to the "duplicate questions" – Neoheurist May 29 '15 at 21:21
  • 3
    @Neoheurist nope, those are the options available in Java. – Luiggi Mendoza May 29 '15 at 21:22
  • Seriously - a down vote - it's a real question - with real intent - I'm sorry that I wasn't born with the Java syntax and best-practices encoded in my DNA – Neoheurist May 29 '15 at 21:23
  • My suspicion is that even when static final objects are defined in java that there is a runtime cost that must be paid for every instantiation of the objects that contain these declarations... so my instinctive aversion to repeatedly paying the instantiation cost seems like it would be minimal (e.g. no real additional overhead -due to the fact that Java is Java (and not C)) – Neoheurist May 29 '15 at 21:28
  • Thanks for the quick answers from those who are able to tolerate the ignorant but ever seeking – Neoheurist May 29 '15 at 21:32
  • 5
    *"is a runtime cost that must be paid for every instantiation"* => not true for `static` properties. There is a small overhead when constructing the class itself (i.e. when it is being loaded and initialized by class loader). But that is "one time only" event. – Pavel Horal May 29 '15 at 21:36
  • 1
    Just remember to do the concatenation in the static array's initializer or a static initialization block. Doing it in a constructor would create the unnecessary per-instantiation payment you were talking about. – RealSkeptic May 29 '15 at 22:00
  • @RealSkeptic yes, I learned something from you - I had no clue regarding the concept of a static initialization block! This is good stuff... I'm very glad that I posted this question... and that you were willing to offer this insight to me... – Neoheurist May 29 '15 at 22:43

3 Answers3

1

You can use Stream like this:

static final String[] GLOBAL_HTML_ATTRIBUTES = {
    "accesskey", "class", "contenteditable", "contextmenu",
    "dir", "draggable", "dropzone", "hidden", "id", "lang",
    "spellcheck", "style", "tabindex", "title", "translate"};

static final String[] A_ELEMENT_ATTRIBUTES = {
    "download", "href", "hreflang", "media", "name", "rel",
    "target", "type"};

static final String[] attributes = Stream.concat(
            Stream.of(GLOBAL_HTML_ATTRIBUTES),
            Stream.of(A_ELEMENT_ATTRIBUTES))
        .collect(Collectors.toList()).toArray(new String[]{});
  • This is an interesting approach - thanks for offering it - my question would regarding the performance costs as compared to other possible approaches – Neoheurist May 29 '15 at 22:39
  • 1
    No need for the `collect(Collectors.toList())`. Stream has [its own toArray method](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#toArray-java.util.function.IntFunction-), so you can just do `Stream.concat(…).toArray(String[]::new)`. – VGR May 29 '15 at 23:45
0

Here's the approach that I find esthetically pleasing (to the eye of an unreformed C developer from way back):

Define a base class in which the "global" Strings reside (as follows)

public class Abstract_Html_Element
{
    private static final List<String> attributes = new ArrayList<String>(Arrays.asList("accesskey", "class", "contenteditable", "contextmenu", "dir", "draggable", "dropzone", "hidden", "id", "lang", "spellcheck", "style", "tabindex", "title", "translate"));

    public Abstract_Html_Element(final List<String> attributes)
    {
        super();

        if (null != attributes)
        {
            Abstract_Html_Element.attributes.addAll(attributes);
        }
    }
}

And then within the implementing subclasses pass an ArrayList of Strings to be concatenated (as follows):

public class A_Element extends Abstract_Html_Element
{
    public static final List<String> attributes = new ArrayList<String>(Arrays.asList("download", "href", "hreflang", "media", "name", "rel", "target", "type"));

    public A_Element()
    {
        super(attributes);
    }
}

The peculiar thing is that the passed attributes are blissfully able to be concatenated to the private final List attributes referenced not by within an instance of the class, but within the Abstract Class itself - this suggests to me that the effect I wanted to achieve of not having to append the two lists of String on every instantiation of the subclass... I have no clue with regard to how to test my hypothesis... but it would be interesting to learn if the compiler actually managed to achieve the effect of appending the String Arrays in a "Singleton" manner...

Neoheurist
  • 3,183
  • 6
  • 37
  • 55
  • Oh man this feels wrong... do you really want to end up with `Abstract_Html_Element` having all atributtes of all subclasses in its `attributes` list? – Pavel Horal May 30 '15 at 08:07
  • @PavelHoral your feeling is correct - I ended up reversing the logic so that the subclass "pulled" the common attribute list from the Abstract_Html_Element... the reason being that adding attributes from any subclass to the abstract (base) class changed the attribute list for all subclasess (unexpected to me because of my mental model regarding where the members of a superclass are actually instantiated)... (my mental model is now corrected) – Neoheurist Jun 11 '15 at 19:09
0

Using the insight offered by @RealSkeptic I refactored as follows:

Abstract "Base" class with "global" Strings

public class Abstract_Html_Element
{
    protected static final List<String> attributes = new ArrayList<String>(Arrays.asList("accesskey", "class", "contenteditable", "contextmenu", "dir", "draggable", "dropzone", "hidden", "id", "lang", "spellcheck", "style", "tabindex", "title", "translate"));

    public Abstract_Html_Element()
    {
        super();
    }
}

Subclass with a static initialization block

public class A_Element extends Abstract_Html_Element
{
    public static final List<String> attributes = new ArrayList<String>(Arrays.asList("download", "href", "hreflang", "media", "name", "rel", "target", "type"));

    { 
        A_Element.attributes.addAll(Abstract_Html_Element.attributes);
    }

    public A_Element()
    {
        super();
    }
}

This is very clean and easy to understand... and will only initialize each list once per execution... (best practice?)

Neoheurist
  • 3,183
  • 6
  • 37
  • 55
  • I find some issues with the code: no need to call defaul parent constructor (`super()` is called by default); no need for empty default constructor; you are using instance initialization block -- prefix it with `static` so that [it is run just once](http://stackoverflow.com/questions/3987428/what-is-an-initialization-block); no need to create `ArrayList` before the initializer block -- you can instantiate that field in the initializer block (it does not matter that it is final when you are operating modifying it from constructors and / or initializer blocks). – Pavel Horal May 30 '15 at 08:04