The correct way to test for a whole array is:
[[ ${ar[@]+set} == set ]] && echo "array has a value may be a null"
What you wrote is the test for the value at index 0. This two are equivalent:
${ar+set}
${ar[0]+set}
But both will only test wetherwhether the array at index zero is set or not, not the whole array, which I assume is what you meant.
But, to your question: Why is an array unset if asigned ()?
Because you have not asigned any value, not even the null, to any array index.
To get some index to be set (and consequently the array) you need something like:
$ declare -a ar=([3]="value")
Or even:
$ declare -a ar=([3]="")
zsh (even in sh or ksh emulation) is the only one of all shells tested (as usual, the special one) that declare an array as set with:
$ unset ar; typeset -a ar=(); echo $(typeset -p ar) ${ar[@]+set}
Question from comments:
Thanks. Can ar[@] be replaced with ar[*] here? Are they array variables?
No, an "${ar[*]}" is the string concatenation of all array values.
However, in this specific expansion: "${ar[*]+set}" both are equivalent.
From the manual (at Arrays):
If subscript is @ or *, the word expands to all members of name.