O que há de errado com o Groovy multi-line String?

105

Os scripts do Groovy geram um erro:

def a = "test"
  + "test"
  + "test"

Erro:

No signature of method: java.lang.String.positive() is 
applicable for argument types: () values: []

Embora este script funcione bem:

def a = new String(
  "test"
  + "test"
  + "test"
)

Por quê?

Yegor256
fonte
5
Caso ajude outra pessoa, você pode obter o mesmo erro com código como def a = b + + "/" + c. Neste caso, os dois símbolos + são obviamente o problema.
Geoff Hackworth

Respostas:

236

Como groovy não tem marcador EOL (como ; ), ele fica confuso se você colocar o operador na linha seguinte

Isso funcionaria em vez disso:

def a = "test" +
  "test" +
  "test"

como o analisador Groovy sabe esperar algo na linha seguinte

O Groovy vê seu original defcomo três instruções separadas. O primeiro atribui testa a, os dois segundos tentam tornar "test"positivo (e é aqui que falha)

Com o new Stringmétodo do construtor, o analisador Groovy ainda está no construtor (já que a chave ainda não foi fechada), então ele pode unir logicamente as três linhas em uma única instrução

Para verdadeiras strings de várias linhas, você também pode usar aspas triplas:

def a = """test
test
test"""

Irá criar uma String com teste em três linhas

Além disso, você pode torná-lo mais organizado:

def a = """test
          |test
          |test""".stripMargin()

o stripMarginmétodo irá cortar a esquerda (até e incluindo o |caractere) de cada linha

tim_yates
fonte
6
Ou simplesmente use stripIndent () em vez de stripMargin().
sschuberth
Sim, e omita os |caracteres nas linhas extras
tim_yates
4
Você pode usar aspas duplas dentro de """strings
tim_yates
3
Certo, editei a resposta para mostrar como você precisaria reformatar um pouco o código para fazê-lo stripIndent()funcionar.
sschuberth
2
Porque a) eu já havia postado como um comentário eb) não é muito diferente dessa resposta.
sschuberth
17

Você pode dizer ao Groovy que a instrução deve ser avaliada além da linha que termina adicionando um par de parênteses ( ... )

def a = ("test"
  + "test"
  + "test")

Uma segunda opção é usar uma barra invertida,, \no final de cada linha:

def a = "test" \
  + "test" \
  + "test"

FWIW, isso é idêntico a como as instruções de várias linhas do Python funcionam.

cmcginty
fonte
A segunda também é como as instruções de várias linhas do VBA funcionam: D (Apenas com sublinhados em vez de barras invertidas, porque VBA.)
Charles Wood
A segunda opção falhou para mim em um Jenkinsfile. A primeira opção funcionou.
SilverJan
Nitpick: a string de várias linhas do Python não requer o operador de concatenação ao usar parênteses, portanto, não é exatamente o mesmo.
Big McLargeHuge
17

Semelhante a stripMargin(), você também pode usar stripIndent () como

def a = """\
        test
        test
        test""".stripIndent()

Por causa de

A linha com o menor número de espaços à esquerda determina o número a ser removido.

você também precisa recuar o primeiro "teste" e não colocá-lo diretamente após o inicial """(isso \garante que a string de várias linhas não comece com uma nova linha).

sschuberth
fonte
2
para que serve?
Joe Phillips,
4
Eu melhorei minha última frase para explicar melhor.
sschuberth