this
this
(иначе говоря, «контекст») - это специальное ключевое слово внутри каждой функции, и его значение зависит только от , как была вызвана функция, а не как / когда / где она была определена. Лексические области не затрагиваются, как и другие переменные. Вот несколько примеров:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
Чтобы узнать больше о this
, просмотрите документацию MDN .
this
this
Фактически вы не хотите иметь доступ к this
в частности, но объект, на который он ссылается на . Вот почему простое решение - просто создать новую переменную, которая также относится к этому объекту. Переменная может иметь любое имя, но общие - self
и that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Поскольку self
является нормальной переменной, она подчиняется лексическим правилам области и доступна внутри обратного вызова. Это также имеет то преимущество, что вы можете получить доступ к значению this
самого обратного вызова.
this
обратного вызова - часть 1 Возможно, у вас есть не контролируйте значение this
, потому что его значение устанавливается автоматически, но на самом деле это не так.
Каждая функция имеет метод .bind
[docs] , который возвращает новую функцию с this
, привязанную к значению. Функция имеет то же поведение, что и тот, который вы назвали .bind
, только то, что this
было установлено вами. Независимо от того, как и когда эта функция вызывается, this
всегда будет ссылаться на переданное значение.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
В этом случае мы привязываем обратный вызов this
к значению MyConstructor
's this
.
Примечание. При связывании контекста для jQuery вместо этого используйте jQuery.proxy
[docs] . Причина этого заключается в том, что вам не нужно сохранять ссылку на функцию при отмене обратного вызова события. jQuery обрабатывает это внутренне.
В ECMAScript 6 представлены функции стрелок , которые можно рассматривать как лямбда-функции. У них нет собственной привязки this
. Вместо этого this
просматривается в области видимости как обычная переменная. Это означает, что вам не нужно называть .bind
. Это не единственное особое поведение, которое у них есть. Дополнительную информацию см. В документации MDN.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
this
обратного вызова - часть 2 Некоторые функции / методы, которые принимают обратные вызовы, также принимают значение, к которому должен обращаться обратный вызов this
. Это в основном то же самое, что и привязывать его самостоятельно, но функция / метод делает это за вас. Array#map
[docs] - такой метод. Его подпись такова:
array.map(callback[, thisArg])
Первый аргумент - это обратный вызов, а второй аргумент - значение this
. Вот надуманный пример:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
Примечание. Можно ли передать значение для this
, как правило, упоминается в документации этой функции / метода. Например, метод $.ajax
jQuery [docs] описывает параметр, называемый context
:
Этот объект станет контекстом всех обратных вызовов, связанных с Ajax.
Общая проблема: использование объектных методов в качестве обработчиков обратных вызовов / событий
Еще одно распространенное проявление этой проблемы - когда объектный метод используется как обработчик обратного вызова / события , Функции являются первоклассными гражданами в JavaScript, а термин «метод» - просто разговорный термин для функции, которая является значением свойства объекта. Но эта функция не имеет конкретной ссылки на ее «содержащий» объект.
Рассмотрим следующий пример:
function Foo() { this.data = 42, document.body.onclick = this.method; } Foo.prototype.method = function() { console.log(this.data); };
Функция
this.method
назначается как обработчик события click , но если щелкнутьdocument.body
, зарегистрированное значение будетundefined
, потому что внутри обработчика событияthis
ссылается наdocument.body
, а не на экземплярFoo
. Как уже упоминалось в начале, то, что относится к [49], зависит от того, как называется функция, а не от того, как она определена. Если код выглядит следующим образом, может быть более очевидно, что функция не имеет неявной ссылки на объект:function method() { console.log(this.data); } function Foo() { this.data = 42, document.body.onclick = this.method; } Foo.prototype.method = method;
Решение такое же, как указано выше: если доступно, используйте
.bind
явно привязатьthis
к определенному значениюdocument.body.onclick = this.method.bind(this);
или явно вызвать функцию как «метод» объекта, используя анонимную функцию в качестве обработчика обратного вызова / события и назначить object (
this
) к другой переменной:var self = this; document.body.onclick = function() { self.method(); };
или использовать функцию стрелки:
document.body.onclick = () => this.method();
Да, это возможно:
List<BigDecimal> bdList = new ArrayList<>();
//populate list
BigDecimal result = bdList.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
Что он делает:
List<BigDecimal>
. Stream<BigDecimal>
BigDecimal.ZERO
. 3.2. Мы указываем BinaryOperator<BigDecimal>
, который добавляет два BigDecimal
, через ссылку на метод BigDecimal::add
. Я вижу, что вы добавили новые данные, поэтому новый ответ станет следующим:
List<Invoice> invoiceList = new ArrayList<>();
//populate
Function<Invoice, BigDecimal> totalMapper = invoice -> invoice.getUnit_price().multiply(invoice.getQuantity());
BigDecimal result = invoiceList.stream()
.map(totalMapper)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Это в основном то же самое, за исключением того, что я добавил переменную totalMapper
, которая имеет функцию от Invoice
до BigDecimal
и возвращает общую стоимость этого счета.
Затем я получаю Stream<Invoice>
, сопоставляю его с Stream<BigDecimal>
, а затем уменьшаю его до BigDecimal
.
Теперь, из точки проектирования ООП, я бы посоветовал вам также использовать метод total()
, который вы уже определили, тогда он становится еще проще:
List<Invoice> invoiceList = new ArrayList<>();
//populate
BigDecimal result = invoiceList.stream()
.map(Invoice::total)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Здесь мы непосредственно используем ссылку на метод в методе map
.
Используйте этот подход для суммирования списка BigDecimal:
List<BigDecimal> values = ... // List of BigDecimal objects
BigDecimal sum = values.stream().reduce((x, y) -> x.add(y)).get();
Этот подход отображает каждый BigDecimal как только BigDecimal и уменьшает их путем суммирования их, который затем возвращается с использованием метода get()
.
Вот еще один простой способ сделать то же суммирование:
List<BigDecimal> values = ... // List of BigDecimal objects
BigDecimal sum = values.stream().reduce(BigDecimal::add).get();
Обновить
Если бы я должен был написать класс и лямбда-выражение в отредактированном вопросе, Я бы написал это следующим образом:
import java.math.BigDecimal;
import java.util.LinkedList;
public class Demo
{
public static void main(String[] args)
{
LinkedList<Invoice> invoices = new LinkedList<>();
invoices.add(new Invoice("C1", "I-001", BigDecimal.valueOf(.1), BigDecimal.valueOf(10)));
invoices.add(new Invoice("C2", "I-002", BigDecimal.valueOf(.7), BigDecimal.valueOf(13)));
invoices.add(new Invoice("C3", "I-003", BigDecimal.valueOf(2.3), BigDecimal.valueOf(8)));
invoices.add(new Invoice("C4", "I-004", BigDecimal.valueOf(1.2), BigDecimal.valueOf(7)));
// Java 8 approach, using Method Reference for mapping purposes.
invoices.stream().map(Invoice::total).forEach(System.out::println);
System.out.println("Sum = " + invoices.stream().map(Invoice::total).reduce((x, y) -> x.add(y)).get());
}
// This is just my style of writing classes. Yours can differ.
static class Invoice
{
private String company;
private String number;
private BigDecimal unitPrice;
private BigDecimal quantity;
public Invoice()
{
unitPrice = quantity = BigDecimal.ZERO;
}
public Invoice(String company, String number, BigDecimal unitPrice, BigDecimal quantity)
{
setCompany(company);
setNumber(number);
setUnitPrice(unitPrice);
setQuantity(quantity);
}
public BigDecimal total()
{
return unitPrice.multiply(quantity);
}
public String getCompany()
{
return company;
}
public void setCompany(String company)
{
this.company = company;
}
public String getNumber()
{
return number;
}
public void setNumber(String number)
{
this.number = number;
}
public BigDecimal getUnitPrice()
{
return unitPrice;
}
public void setUnitPrice(BigDecimal unitPrice)
{
this.unitPrice = unitPrice;
}
public BigDecimal getQuantity()
{
return quantity;
}
public void setQuantity(BigDecimal quantity)
{
this.quantity = quantity;
}
}
}
get()
, когда он возвращает значение Optional
, которое возвращается вызовом reduce
. Если вы хотите работать с Optional
или просто распечатать сумму, тогда да, get()
не требуется. Но печать опций напрямую печатает синтаксис на основе Optional[<Value>]
, который я сомневаюсь в необходимости пользователя. Поэтому get()
необходим для получения значения из Optional
.
– Aman Agnihotri
25 March 2014 в 15:56
get
! Если values
является пустым списком, опциональный не будет содержать значения и будет вызывать NoSuchElementException
, когда вызывается get
. Вместо этого вы можете использовать values.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
.
– eee
2 April 2016 в 10:31
Я придумал следующую реализацию Collector для решения этой проблемы. Это написано в Groovy, поэтому вам придется адаптировать его, если вы используете только Java, но у него есть преимущество в поддержке потока произвольных типов, если эти типы поддерживаются ctor BigDecimal:
public static <T> Collector<T, ?, BigDecimal> summingBigDecimal() {
new java.util.stream.Collectors.CollectorImpl<?, ?, BigDecimal>(
{ [BigDecimal.ZERO].toArray(new BigDecimal[1]) },
{ BigDecimal[] a, Object t ->
a[0] = (t instanceof BigDecimal ? a[0].add(t) : a[0].add(new BigDecimal(t)))
},
{ BigDecimal[] a, BigDecimal[] b -> a[0].add(b[0]) },
{ BigDecimal[] a -> a[0] }, Collections.emptySet());
}
Я уверен, что его можно немного очистить, но он может делать такие вещи, как:
Stream.of("1", 3L, new BigDecimal("5")).collect(Collectors.summingBigDecimal())
... оказался полезным в определенных ситуациях, когда я не хочу, чтобы меня беспокоили сделайте преобразование самостоятельно.
java.util.stream.Collectors.CollectorImpl
. Или для обеспечения ужасно сложного решения вопроса, который уже получил гораздо более простое решение за несколько лет до вашего ответа. Или для размещения кода Groovy для Java-вопроса.
– Holger
12 July 2018 в 17:43
Если вы не возражаете против зависимости третьей стороны, в Collectors2 есть класс с именем Коллекции Eclipse , который содержит методы, возвращающие коллекторы для суммирования и , суммируя BigDecimal и BigInteger. Эти методы принимают функцию в качестве параметра, поэтому вы можете извлечь значение BigDecimal или BigInteger из объекта.
List<BigDecimal> list = mList(
BigDecimal.valueOf(0.1),
BigDecimal.valueOf(1.1),
BigDecimal.valueOf(2.1),
BigDecimal.valueOf(0.1));
BigDecimal sum =
list.stream().collect(Collectors2.summingBigDecimal(e -> e));
Assert.assertEquals(BigDecimal.valueOf(3.4), sum);
BigDecimalSummaryStatistics statistics =
list.stream().collect(Collectors2.summarizingBigDecimal(e -> e));
Assert.assertEquals(BigDecimal.valueOf(3.4), statistics.getSum());
Assert.assertEquals(BigDecimal.valueOf(0.1), statistics.getMin());
Assert.assertEquals(BigDecimal.valueOf(2.1), statistics.getMax());
Assert.assertEquals(BigDecimal.valueOf(0.85), statistics.getAverage());
Примечание: я являюсь коммиттером для коллекций Eclipse.
Вы можете суммировать значения потока BigDecimal
с использованием многоразового коллектора с именем summingUp
:
BigDecimal sum = bigDecimalStream.collect(summingUp());
. Collector
можно реализовать следующим образом: :
public static Collector<BigDecimal, ?, BigDecimal> summingUp() {
return Collectors.reducing(BigDecimal.ZERO, BigDecimal::add);
}
У этого сообщения уже есть проверенный ответ, но ответ не фильтрует для нулевых значений. Правильный ответ должен предотвращать пустые значения, используя функцию Object :: nonNull как предикат.
BigDecimal result = invoiceList.stream()
.map(Invoice::total)
.filter(Objects::nonNull)
.filter(i -> (i.getUnit_price() != null) && (i.getQuantity != null))
.reduce(BigDecimal.ZERO, BigDecimal::add);
Это предотвращает попытку суммирования нулевых значений по мере уменьшения.
Invoice::total
vsinvoice -> invoice.total()
. – ryvantage 25 March 2014 в 20:50