Ответ 1
Нет, структура, которую вы обнаружили, - это то, как Java обрабатывает ее (то есть с перегрузкой вместо параметров по умолчанию).
Я наткнулся на некоторый Java-код, который имел следующую структуру:
public MyParameterizedFunction(String param1, int param2)
{
this(param1, param2, false);
}
public MyParameterizedFunction(String param1, int param2, boolean param3)
{
//use all three parameters here
}
Я знаю, что в С++ я могу назначить параметр по умолчанию. Например:
void MyParameterizedFunction(String param1, int param2, bool param3=false);
Поддерживает ли Java такой синтаксис? Есть ли причины, почему этот двухэтапный синтаксис предпочтительнее?
Нет, структура, которую вы обнаружили, - это то, как Java обрабатывает ее (то есть с перегрузкой вместо параметров по умолчанию).
Нет, но вы можете использовать Builder Pattern, как описано в этот ответ.
Как описано в связанном ответе, шаблон Builder позволяет писать код типа
Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
.name("Spicoli")
.age(16)
.motto("Aloha, Mr Hand")
.buildStudent();
в котором некоторые поля могут иметь значения по умолчанию или иначе быть необязательными.
Есть несколько способов симулировать параметры по умолчанию в Java:
Метод перегрузки.
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");
Одним из ограничений этого подхода является то, что он не работает, если у вас есть два необязательных параметра одного типа, и любой из них может быть опущен.
С переменным числом аргументов.
а) Все необязательные параметры имеют одинаковый тип:
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);
б) Типы необязательных параметров могут быть разными:
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");
Основным недостатком этого подхода является то, что если дополнительные параметры имеют разные типы, вы теряете статическую проверку типов. Кроме того, если каждый параметр имеет разное значение, вам нужен какой-то способ их различения.
Нулевые. Чтобы устранить ограничения предыдущих подходов, вы можете разрешить нулевые значения, а затем проанализировать каждый параметр в теле метода:
void foo(String a, Integer b, Integer c) {
b = b != null ? b : 0;
c = c != null ? c : 0;
//...
}
foo("a", null, 2);
Теперь все значения аргументов должны быть предоставлены, но значения по умолчанию могут быть нулевыми.
Необязательный класс. Этот подход аналогичен пустым значениям, но использует необязательный класс Java 8 для параметров со значением по умолчанию:
void foo(String a, Optional<Integer> bOpt) {
Integer b = bOpt.isPresent() ? bOpt.get() : 0;
//...
}
foo("a", Optional.of(2));
foo("a", Optional.<Integer>absent());
Необязательно делает контракт метода явным для вызывающей стороны, однако, такая сигнатура может оказаться слишком многословной.
Образец строителя. Шаблон Builder используется для конструкторов и реализуется путем введения отдельного класса Builder:
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();
Карты. Если число параметров слишком велико и для большинства из них обычно используются значения по умолчанию, вы можете передать аргументы метода в виде карты их имен/значений:
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"));
Обратите внимание, что вы можете комбинировать любой из этих подходов для достижения желаемого результата.
К сожалению, нет.
К сожалению, да.
void MyParameterizedFunction(String param1, int param2, bool param3=false) {}
может быть записана в Java 1.5 как:
void MyParameterizedFunction(String param1, int param2, Boolean... params) {
assert params.length <= 1;
bool param3 = params.length > 0 ? params[0].booleanValue() : false;
}
Но следует ли вам зависеть от того, как вы относитесь к компилятору, генерирующему
new Boolean[]{}
для каждого вызова.
Для нескольких параметров по умолчанию:
void MyParameterizedFunction(String param1, int param2, bool param3=false, int param4=42) {}
может быть записана в Java 1.5 как:
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;
}
Это соответствует синтаксису С++, который разрешает только дефолтные параметры в конце списка параметров.
Помимо синтаксиса, существует разница, когда это проверяет тип времени выполнения для переданных параметров по умолчанию, а тип С++ проверяет их во время компиляции.
Нет, но вы можете легко имитировать их. Что в С++ было:
public: void myFunction(int a, int b=5, string c="test") { ... }
В Java это будет перегруженная функция:
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);
}
Ранее упоминалось, что параметры по умолчанию вызывали неоднозначные случаи при перегрузке функции. Это просто неверно, мы можем видеть в случае С++: да, возможно, он может создавать неоднозначные случаи, но эту проблему можно легко обработать. Он просто не был разработан на Java, вероятно, потому, что создатели хотели гораздо более простой язык, чем С++, - если бы они были права, это еще один вопрос. Но большинство из нас не думает, что он использует Java из-за своей простоты.
Вы можете сделать это в Scala, который работает на JVM и совместим с программами Java. http://www.scala-lang.org/
то есть.
class Foo(var prime: Boolean = false, val rib: String) {}
Я мог бы заявить очевидное здесь, но почему бы просто не реализовать параметр "default" самостоятельно?
public class Foo() {
public void func(String s){
func(s, true);
}
public void func(String s, boolean b){
//your code here
}
}
по умолчанию вы будете использовать эфир
func ( "моя строка" );
и если вы не хотите использовать значение по умолчанию, вы должны использовать
func ( "моя строка", false);
Нет, но самый простой способ реализовать это:
public myParameterizedFunction(String param1, int param2, Boolean param3) {
param3 = param3 == null ? false : param3;
}
public myParameterizedFunction(String param1, int param2) {
this(param1, param2, false);
}
или вместо троичного оператора вы можете использовать, if
:
public myParameterizedFunction(String param1, int param2, Boolean param3) {
if (param3 == null) {
param3 = false;
}
}
public myParameterizedFunction(String param1, int param2) {
this(param1, param2, false);
}
Нет.
Вы можете добиться такого же поведения, передав объект с интеллектуальными значениями по умолчанию. Но опять-таки это зависит от вашего дела.
Нет. В общем, у Java нет большого (любого) синтаксического сахара, поскольку они пытались сделать простой язык.
Как упоминалось в Scala, стоит упомянуть и о Kotlin. В параметрах функции Kotlin также могут быть значения по умолчанию, и они могут даже ссылаться на другие параметры:
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
...
}
Как и Scala, Kotlin работает на JVM и может быть легко интегрирован в существующие проекты Java.
Он не поддерживается, но есть несколько параметров, таких как использование шаблона объекта параметров с некоторым синтаксическим сахаром:
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) {
}
}
В этом примере мы построим ParameterObject
со значениями по умолчанию и переопределим их в разделе инициализации экземпляра класса { param1 = 10; param2 = "bar";}
Попробуйте это решение:
public int getScore(int score, Integer... bonus)
{
if(bonus.length > 0)
{
return score + bonus[0];
}
return score;
}
Вы можете использовать Java Invoice Builder для автоматического создания построителя со значениями по умолчанию.
Просто добавьте @GenerateMethodInvocationBuilder к классу или интерфейсу и @Default к параметрам в методах, где вы хотите значения по умолчанию. Во время компиляции будет создан строитель, используя значения по умолчанию, указанные вами в аннотациях.
@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;
}
}
И затем вы можете вызвать методы.
CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
.invoke(instance);
Или установите любое значение по умолчанию для чего-то еще.
CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
.withColor(Color.YELLOW)//
.invoke(instance);
Аналогичный подход к fooobar.com/questions/1213/..., который работает в Java 8, заключается в использовании интерфейса с использованием гейтеров по умолчанию. Это будет более условно, но является макетным, и это здорово, когда у вас есть куча примеров, где вы действительно хотите привлечь внимание к параметрам.
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; }
});
}
}
Есть полдюжины или более важные проблемы, такие как это, в конечном итоге вы попадаете в статический шаблон factory... см. криптографию api для этого. Сортировка сложно объяснить, но подумайте об этом так: если у вас есть конструктор, по умолчанию или иначе, единственный способ распространения состояния за фигурными фигурными скобками - либо иметь логическое значение isValid; (вместе с нулевым значением по умолчанию v сбойным конструктором) или генерирует исключение, которое никогда не бывает информативным, когда оно возвращается от пользователей поля.
Код. Исправьте, я пишу тысячи конструкторов строк и делаю то, что мне нужно. Я нахожу использование isValid при построении объекта, другими словами, двухстрочных конструкторов, но по какой-то причине я переношусь на статический шаблон factory. Мне кажется, вы можете много сделать, если вы в вызове метода, есть еще проблемы с синхронизацией(), но значения по умолчанию могут быть "заменены" лучше (безопаснее)
Я думаю, что нам нужно сделать здесь ошибку null в качестве значения по умолчанию vis-a-vis что-то String one = new String (""); в качестве переменной-члена, а затем выполнить проверку на нуль перед назначением строки, переданной конструктору.
Очень примечательно количество сырой, стратосферной компьютерной науки, сделанной на Java.
С++ и т.д. есть поставщики libs, да. Java может обогнать их на больших серверах благодаря массивной панели инструментов. Изучите блоки статического инициализатора, оставайтесь с нами.
Вот как я это сделал... это не так удобно, как наличие "необязательного аргумента" против вашего определенного параметра, но он выполняет свою работу:
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");
}
Заметьте, что я могу вызывать одно и то же имя метода либо с помощью строки, либо я могу вызвать ее со строкой и логическим значением. В этом случае установка wipeClean в true заменит весь текст в моей TextArea на предоставленную строку. Установка wipeClean на false или оставление его вместе просто добавляет предоставленный текст в TextArea.
Также обратите внимание, что я не повторяю код в двух методах, я просто добавляю функциональность возможности reset TextArea, создавая новый метод с тем же именем только с добавленным булевым.
Я на самом деле думаю, что это немного чище, чем если бы Java предоставила "необязательный аргумент" для наших параметров, так как нам нужно было бы затем кодировать значения по умолчанию и т.д. В этом примере мне не нужно беспокоиться ни о какой из этих, Да, я добавил еще один метод для своего класса, но его легче читать в долгосрочной перспективе по моему скромному мнению.
NO, но у нас есть альтернатива в виде перегрузки функций.
когда не передан параметр
void operation(){
int a = 0;
int b = 0;
}
вызываемый при передаче "a"
void operation(int a){
int b = 0;
//code
}
вызываемый при прохождении параметра b
void operation(int a , int b){
//code
}
Сейчас я потратил довольно много времени, чтобы выяснить, как использовать это с методами, которые возвращают значения, и я не видел пока никаких примеров, я подумал, что было бы полезно добавить это здесь:
int foo(int a) {
// do something with a
return a;
}
int foo() {
return foo(0); // here, 0 is a default value for a
}