Pergunta

me deparei com algum código Java que tinha a seguinte estrutura:

public MyParameterizedFunction(String param1, int param2)
{
    this(param1, param2, false);
}

public MyParameterizedFunction(String param1, int param2, boolean param3)
{
    //use all three parameters here
}

Eu sei que em C ++ posso atribuir um parâmetro um valor padrão. Por exemplo:

void MyParameterizedFunction(String param1, int param2, bool param3=false);

O Java apoiar este tipo de sintaxe? Existem quaisquer razões pelas quais esta sintaxe duas etapas é preferível?

Foi útil?

Solução

Não, a estrutura que você encontrou é como Java lida com ele (isto é, com a sobrecarga em vez de parâmetros padrão).

Para construtores, Ver Effective Java: Programação item 1 ponta (Considere métodos de fábrica estáticos em vez de construtores) se a sobrecarga está ficando complicada. Para outros métodos, renomeando alguns casos ou usando um parâmetro de objeto pode ajudar. Isto é, quando você tem suficiente complexidade que a diferenciação é difícil. Um caso definitivo é onde você tem que diferenciar usando a ordem dos parâmetros, e não apenas o número e tipo.

Outras dicas

Não, mas você pode usar o Builder Padrão , conforme descrito em this Stack Overflow resposta .

Como descrito na resposta ligada, o padrão Builder permite que você escrever código como

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

em que alguns campos podem ter valores padrão ou não ser opcional.

Existem várias maneiras de parâmetros padrão simular em Java:

  1. Método sobrecarga.

    void foo(String a, Integer b) {
        //...
    }
    
    void foo(String a) {
        foo(a, 0); // here, 0 is a default value for b
    }
    
    foo("a", 2);
    foo("a");
    

    Uma das limitações dessa abordagem é que ela não funciona se você tem dois parâmetros opcionais do mesmo tipo e qualquer uma delas pode ser omitida.

  2. VarArgs.

    a) Todos os parâmetros opcionais são do mesmo tipo:

    void foo(String a, Integer... b) {
        Integer b1 = b.length > 0 ? b[0] : 0;
        Integer b2 = b.length > 1 ? b[1] : 0;
        //...
    }
    
    foo("a");
    foo("a", 1, 2);
    

    b) Tipos de parâmetros opcionais podem ser diferentes:

    void foo(String a, Object... b) {
        Integer b1 = 0;
        String b2 = "";
        if (b.length > 0) {
          if (!(b[0] instanceof Integer)) { 
              throw new IllegalArgumentException("...");
          }
          b1 = (Integer)b[0];
        }
        if (b.length > 1) {
            if (!(b[1] instanceof String)) { 
                throw new IllegalArgumentException("...");
            }
            b2 = (String)b[1];
            //...
        }
        //...
    }
    
    foo("a");
    foo("a", 1);
    foo("a", 1, "b2");
    

    A principal desvantagem desta abordagem é que, se os parâmetros opcionais são de tipos diferentes que você perde a verificação de tipo estático. Além disso, se cada parâmetro tem um significado diferente, você precisa de alguma maneira para distingui-los.

  3. nulos Para resolver as limitações das abordagens anteriores, você pode permitir que os valores nulos e, em seguida, analisar cada parâmetro em um corpo de método:.

    void foo(String a, Integer b, Integer c) {
        b = b != null ? b : 0;
        c = c != null ? c : 0;
        //...
    }
    
    foo("a", null, 2);
    

    Agora, todos os valores de argumentos devem ser fornecidos, mas o padrão que pode ser nulo.

  4. classe Opcional Esta abordagem é semelhante à nulos, mas usa Java 8 classe opcional para parâmetros que têm um valor padrão:.

    void foo(String a, Optional<Integer> bOpt) {
        Integer b = bOpt.isPresent() ? bOpt.get() : 0;
        //...
    }
    
    foo("a", Optional.of(2));
    foo("a", Optional.<Integer>absent());
    

    Opcional faz um contrato método explícito para um chamador, no entanto, pode-se encontrar tal assinatura muito detalhado.

  5. padrão Builder O padrão do construtor é usado para construtores e é implementado através da introdução de uma classe Builder separado:.

     class Foo {
         private final String a; 
         private final Integer b;
    
         Foo(String a, Integer b) {
           this.a = a;
           this.b = b;
         }
    
         //...
     }
    
     class FooBuilder {
       private String a = ""; 
       private Integer b = 0;
    
       FooBuilder setA(String a) {
         this.a = a;
         return this;
       }
    
       FooBuilder setB(Integer b) {
         this.b = b;
         return this;
       }
    
       Foo build() {
         return new Foo(a, b);
       }
     }
    
     Foo foo = new FooBuilder().setA("a").build();
    
  6. mapas Quando o número de parâmetros é muito grande e para a maioria deles os valores padrão são normalmente utilizados, você pode passar argumentos de método como um mapa de seus nomes / valores:.

    void foo(Map<String, Object> parameters) {
        String a = ""; 
        Integer b = 0;
        if (parameters.containsKey("a")) { 
            if (!(parameters.get("a") instanceof Integer)) { 
                throw new IllegalArgumentException("...");
            }
            a = (String)parameters.get("a");
        } else if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>of(
        "a", "a",
        "b", 2, 
        "d", "value")); 
    

Por favor, note que você pode combinar qualquer uma dessas abordagens para alcançar um resultado desejável.

Infelizmente, não.

Infelizmente, sim.

void MyParameterizedFunction(String param1, int param2, bool param3=false) {}

poderia ser escrito em Java 1.5 como:

void MyParameterizedFunction(String param1, int param2, Boolean... params) {
    assert params.length <= 1;
    bool param3 = params.length > 0 ? params[0].booleanValue() : false;
}

Mas se você deve ou não depende de como você se sente sobre a geração de compilador um

new Boolean[]{}

para cada chamada.

Para vários parâmetros padronizáveis:

void MyParameterizedFunction(String param1, int param2, bool param3=false, int param4=42) {}

poderia ser escrito em Java 1.5 como:

void MyParameterizedFunction(String param1, int param2, Object... p) {
    int l = p.length;
    assert l <= 2;
    assert l < 1 || Boolean.class.isInstance(p[0]);
    assert l < 2 || Integer.class.isInstance(p[1]);
    bool param3 = l > 0 && p[0] != null ? ((Boolean)p[0]).booleanValue() : false;
    int param4 = l > 1 && p[1] != null ? ((Integer)p[1]).intValue() : 42;
}

Esta corresponde sintaxe C ++, que só permite que os parâmetros inadimplentes no final da lista de parâmetros.

sintaxe Beyond, há uma diferença quando este tenha executar a verificação de tipo tempo para parâmetros padronizáveis ??passaram e C ++ tipo verifica-los durante a compilação.

Não, mas você pode muito facilmente imitá-los. O que em C ++ foi:

public: void myFunction(int a, int b=5, string c="test") { ... }

Em Java, que será uma função sobrecarregada:

public void myFunction(int a, int b, string c) { ... }

public void myFunction(int a, int b) {
    myFunction(a, b, "test");
}

public void myFunction(int a) {
    myFunction(a, 5);
}

No início foi mencionado, que os parâmetros padrão causado casos ambíguos em função de sobrecarga. Isso simplesmente não é verdade, podemos ver no caso do C ++: sim, talvez ele pode criar casos ambíguos, mas estes problema pode ser facilmente manipulados. Ele simplesmente não foi desenvolvido em Java, provavelmente porque os criadores queriam uma linguagem mais simples, como C ++ foi - se tivessem direito, é outra questão. Mas a maioria de nós não acho que ele usa Java devido à sua simplicidade.

Você pode fazer isso está em Scala, que é executado na JVM e é compatível com os programas Java. http://www.scala-lang.org/

i.

class Foo(var prime: Boolean = false, val rib: String)  {}

Não , mas a maneira mais simples de implementar esta é a seguinte:

public myParameterizedFunction(String param1, int param2, Boolean param3) {

    param3 = param3 == null ? false : param3;
}

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

ou em vez do operador ternário , você pode usar if:

public myParameterizedFunction(String param1, int param2, Boolean param3) {

    if (param3 == null) {
        param3 = false;
    }
}

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

Eu poderia estar afirmando o óbvio aqui, mas por que não simplesmente implementar o "default" parâmetro si mesmo?

public class Foo() {
        public void func(String s){
                func(s, true);
        }
        public void func(String s, boolean b){
                //your code here
        }
}

para o padrão que seria de éter uso

func ( "minha string");

e se você não gostaria de usar o padrão, você usaria

func ( "minha string", false);

No. Em Java geral não tem muito (qualquer) açúcar sintático, já que eles tentaram fazer uma linguagem simples.

No.

Você pode conseguir o mesmo comportamento, passando um objeto que tem padrões inteligentes. Mas, novamente, depende do que o seu caso está na mão.

Como Scala foi mencionado, Kotlin Também vale a pena mencionar. Em Kotlin parâmetros de função podem ter valores padrão bem e eles podem até se referir a outros parâmetros:

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
    ...
}

Como Scala, Kotlin é executado na JVM e pode ser facilmente integrado em projetos Java existentes.

Não é suportado, mas há várias opções como o uso de padrão de parâmetro de objeto com um pouco de açúcar sintaxe:

public class Foo() {
    private static class ParameterObject {
        int param1 = 1;
        String param2 = "";
    }

    public static void main(String[] args) {
        new Foo().myMethod(new ParameterObject() {{ param1 = 10; param2 = "bar";}});
    }

    private void myMethod(ParameterObject po) {
    }
}

Neste exemplo vamos construir ParameterObject com valores padrão e substituí-los na seção de inicialização de instância de classe { param1 = 10; param2 = "bar";}

Tente esta solução:

public int getScore(int score, Integer... bonus)
{
    if(bonus.length > 0)
    {
        return score + bonus[0];
    }

    return score;
}

Você pode usar Java Method Invocation Builder para gerar automaticamente o construtor com padrão valores.

Basta adicionar @GenerateMethodInvocationBuilder para a classe ou interface, eo @Default aos parâmetros em métodos de onde você deseja que os valores padrão. Um construtor será gerada em tempo de compilação, usando os valores padrão que você especificou com suas anotações.

@GenerateMethodInvocationBuilder
public class CarService {
 public CarService() {
 }

 public String getCarsByFilter(//
   @Default("Color.BLUE") Color color, //
   @Default("new ProductionYear(2001)") ProductionYear productionYear,//
   @Default("Tomas") String owner//
 ) {
  return "Filtering... " + color + productionYear + owner;
 }
}

E então você pode chamar os métodos.

CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
  .invoke(instance);

Ou definir qualquer um dos valores padrão para outra coisa.

CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
  .withColor(Color.YELLOW)//
  .invoke(instance);

Uma abordagem semelhante à https://stackoverflow.com/a/13864910/2323964 que funciona em Java 8 é usar uma interface com getters padrão. Este será mais espaços em branco detalhado, mas é mockable, e é ótimo para quando você tem um monte de casos onde você realmente querem chamar a atenção para os parâmetros.

public class Foo() {
    public interface Parameters {
        String getRequired();
        default int getOptionalInt(){ return 23; }
        default String getOptionalString(){ return "Skidoo"; }
    }

    public Foo(Parameters parameters){
        //...
    }

    public static void baz() {
        final Foo foo = new Foo(new Person() {
            @Override public String getRequired(){ return "blahblahblah"; }
            @Override public int getOptionalInt(){ return 43; }
        });
    }
}

Eu já passei algum tempo para descobrir como usar isso com métodos que retornam valores, e eu não vi qualquer exemplos até agora, eu pensei que poderia ser útil para adicionar este aqui:

int foo(int a) {
    // do something with a
    return a;
}

int foo() {
    return foo(0); // here, 0 is a default value for a
}

Há meia dúzia ou melhor questões como esta, eventualmente, chegar ao padrão de fábrica estática ... ver a API de criptografia para isso. Ordenar difícil de explicar, mas pense nisso desta maneira: Se você tem um construtor padrão, ou de outra forma, a única maneira de propagar estado além das chaves é ou ter um booleano isValid; (Juntamente com o nulo como valor padrão v construtor falhou) ou lançar uma exceção que nunca é informativa quando ficar para trás a partir de usuários de campo.

Code estar correto condenado, eu escrevo mil construtores de linha e fazer o que eu preciso. Acho usando isValid na construção do objeto - em outras palavras, dois construtores de linha -, mas por alguma razão eu estou migrando para o padrão de fábrica estático. Eu só parece que você pode fazer muito se você em uma chamada de método, ainda há sync () questões, mas padrões podem ser 'substituído' melhor (mais seguro)

Eu acho que o que precisamos fazer aqui é abordem a questão das nulo como valor padrão vis-a-vis algo Cordas um = new String ( ""); como uma variável de membro, em seguida, fazer uma verificação para nulo antes de atribuir cadeia transmitida para o construtor.

Muito notável a quantidade de ciência da computação cru, estratosférico feito em Java.

C ++ e assim por diante tem libs vendedor, sim. Java pode ultrapassá-los em servidores de grande escala devido à sua caixa de ferramentas enorme. Estudo estático initializer blocos, estadia conosco.

Isto é como eu fiz isso ... não é tão conveniente, talvez como tendo um 'argumento opcional' contra o seu parâmetro definido, mas ele começa o trabalho feito:

public void postUserMessage(String s,boolean wipeClean)
{
    if(wipeClean)
    {
        userInformation.setText(s + "\n");
    }
    else
    {
        postUserMessage(s);
    }
}

public void postUserMessage(String s)
{
    userInformation.appendText(s + "\n");
}

Observe que pode invocar o mesmo nome do método ou com apenas uma corda ou posso chamá-lo com uma corda e um valor booleano. Neste caso, a definição wipeClean a verdade irá substituir todo o texto no meu TextArea com a string fornecido. Definir wipeClean como falsa ou deixá-lo todos juntos simplesmente acrescenta o texto fornecido ao TextArea.

Observe também não estou repetindo código nos dois métodos, estou apenas adicionando a funcionalidade de ser capaz de redefinir a TextArea através da criação de um novo método com o mesmo nome somente com o boolean acrescentou.

Na verdade, eu acho que isso é um pouco mais limpo do que se Java fornecido um 'argumento opcional' para os nossos parâmetros, uma vez que seria necessário, em seguida, o código para valores padrão etc. Neste exemplo, eu não precisa se preocupar com nada disso . Sim, eu adicionei mais um método para minha classe, mas é mais fácil de ler, a longo prazo na minha humilde opinião.

Não, mas temos alternativa na forma de sobrecarga de funções.

chamado quando nenhum parâmetro passado

void operation(){

int a = 0;
int b = 0;

} 

chamado quando "a" parâmetro foi aprovada

void operation(int a){

int b = 0;
//code

} 

chamado quando o parâmetro b passou

void operation(int a , int b){
//code
} 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top