takeWhile работает нормально, вам понадобится что-то вроде этого:
const stream = interval(1000)
.pipe(
takeWhile(() => messages.length > 0),
map(() => messages.pop()),
);
Я сделал небольшой пример на stackblitz, надеюсь, вы можете использовать это в своем приложении: https: // stackblitz .com / редактировать / машинопись-sq6wxb? файл = index.ts
Престижность при обучении на практике. Вот предложения о «возможностях» для улучшения:
Может существовать только один тип кортежей (если установлена блокировка типа). Это ухудшает возможности повторного использования и масштабируемости в программах, желающих использовать несколько типов кортежей, если только вы не прибегаете к повторному использованию «вырезать и вставить» (BirthdayTuple, DimensionsTuple, StreetAddressTuple, ...). Рассмотрим класс TupleFactory, который принимает целевые типы и создает объект конструктора кортежей для генерации кортежей.
Допустимость «null» как значения в кортеже не задокументирована. Я думаю, что до того, как Typelock установлен, null разрешен; но после установки Typelock код сгенерирует исключение NullPointerException - это противоречиво. Если они не разрешены, конструктор должен их перехватить и запретить (независимо от Typelock). Если они разрешены, тогда код в целом (конструктор, равно, хэш-код и т. Д.) Нуждается в модификации, чтобы сделать это возможным.
Решите, должны ли кортежи быть объектами неизменных значений. На основании отсутствия методов установки, я бы предположил, что так. Если это так, то будьте осторожны с «принятием» входящего массива - lastTuple=this.arr
. Несмотря на то, что это конструктор var arg, конструктор может быть вызван напрямую с массивом. Класс принимает массив (сохраняет ссылку на него), и значения в массиве могут быть впоследствии изменены вне класса. Я сделал бы мелкую копию массива, но также задокументировал бы потенциальную проблему с кортежами с неизменяемыми значениями (которые могли быть изменены вне кортежей).
В вашем методе equals
отсутствует проверка нуля (if (obj == null) return false
) и проверка класса (либо obj instanceof Tuple
, либо this.getClass().equals(object.getClass())
). Идиома равенства одинаково хорошо документирована.
Нет способа просмотреть значения кортежа, кроме как через toString
. Это защищает ценности и общую неизменность, но я думаю, что это ограничивает полезность класса.
Хотя я понимаю, что это всего лишь пример, я не ожидал бы использовать этот класс для чего-то вроде дней рождения / дат. В доменах решений с фиксированными типами объектов реальные классы (например, Date) намного лучше. Я хотел бы представить этот класс полезным в определенных областях, где кортежи являются объектами первого класса.
Редактировать Думаю об этом. Вот мой взгляд на некоторый код (на github + tests ):
===
Tuple.java
===
package com.stackoverflow.tuple;
/**
* Tuple are immutable objects. Tuples should contain only immutable objects or
* objects that won't be modified while part of a tuple.
*/
public interface Tuple {
public TupleType getType();
public int size();
public <T> T getNthValue(int i);
}
===
TupleType.java
===
package com.stackoverflow.tuple;
/**
* Represents a type of tuple. Used to define a type of tuple and then
* create tuples of that type.
*/
public interface TupleType {
public int size();
public Class<?> getNthType(int i);
/**
* Tuple are immutable objects. Tuples should contain only immutable objects or
* objects that won't be modified while part of a tuple.
*
* @param values
* @return Tuple with the given values
* @throws IllegalArgumentException if the wrong # of arguments or incompatible tuple values are provided
*/
public Tuple createTuple(Object... values);
public class DefaultFactory {
public static TupleType create(final Class<?>... types) {
return new TupleTypeImpl(types);
}
}
}
===
TupleImpl.java (not visible outside package)
===
package com.stackoverflow.tuple;
import java.util.Arrays;
class TupleImpl implements Tuple {
private final TupleType type;
private final Object[] values;
TupleImpl(TupleType type, Object[] values) {
this.type = type;
if (values == null || values.length == 0) {
this.values = new Object[0];
} else {
this.values = new Object[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
}
}
@Override
public TupleType getType() {
return type;
}
@Override
public int size() {
return values.length;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getNthValue(int i) {
return (T) values[i];
}
@Override
public boolean equals(Object object) {
if (object == null) return false;
if (this == object) return true;
if (! (object instanceof Tuple)) return false;
final Tuple other = (Tuple) object;
if (other.size() != size()) return false;
final int size = size();
for (int i = 0; i < size; i++) {
final Object thisNthValue = getNthValue(i);
final Object otherNthValue = other.getNthValue(i);
if ((thisNthValue == null && otherNthValue != null) ||
(thisNthValue != null && ! thisNthValue.equals(otherNthValue))) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 17;
for (Object value : values) {
if (value != null) {
hash = hash * 37 + value.hashCode();
}
}
return hash;
}
@Override
public String toString() {
return Arrays.toString(values);
}
}
===
TupleTypeImpl.java (not visible outside package)
===
package com.stackoverflow.tuple;
class TupleTypeImpl implements TupleType {
final Class<?>[] types;
TupleTypeImpl(Class<?>[] types) {
this.types = (types != null ? types : new Class<?>[0]);
}
public int size() {
return types.length;
}
//WRONG
//public <T> Class<T> getNthType(int i)
//RIGHT - thanks Emil
public Class<?> getNthType(int i) {
return types[i];
}
public Tuple createTuple(Object... values) {
if ((values == null && types.length == 0) ||
(values != null && values.length != types.length)) {
throw new IllegalArgumentException(
"Expected "+types.length+" values, not "+
(values == null ? "(null)" : values.length) + " values");
}
if (values != null) {
for (int i = 0; i < types.length; i++) {
final Class<?> nthType = types[i];
final Object nthValue = values[i];
if (nthValue != null && ! nthType.isAssignableFrom(nthValue.getClass())) {
throw new IllegalArgumentException(
"Expected value #"+i+" ('"+
nthValue+"') of new Tuple to be "+
nthType+", not " +
(nthValue != null ? nthValue.getClass() : "(null type)"));
}
}
}
return new TupleImpl(this, values);
}
}
===
TupleExample.java
===
package com.stackoverflow.tupleexample;
import com.stackoverflow.tuple.Tuple;
import com.stackoverflow.tuple.TupleType;
public class TupleExample {
public static void main(String[] args) {
// This code probably should be part of a suite of unit tests
// instead of part of this a sample program
final TupleType tripletTupleType =
TupleType.DefaultFactory.create(
Number.class,
String.class,
Character.class);
final Tuple t1 = tripletTupleType.createTuple(1, "one", 'a');
final Tuple t2 = tripletTupleType.createTuple(2l, "two", 'b');
final Tuple t3 = tripletTupleType.createTuple(3f, "three", 'c');
final Tuple tnull = tripletTupleType.createTuple(null, "(null)", null);
System.out.println("t1 = " + t1);
System.out.println("t2 = " + t2);
System.out.println("t3 = " + t3);
System.out.println("tnull = " + tnull);
final TupleType emptyTupleType =
TupleType.DefaultFactory.create();
final Tuple tempty = emptyTupleType.createTuple();
System.out.println("\ntempty = " + tempty);
// Should cause an error
System.out.println("\nCreating tuple with wrong types: ");
try {
final Tuple terror = tripletTupleType.createTuple(1, 2, 3);
System.out.println("Creating this tuple should have failed: "+terror);
} catch (IllegalArgumentException ex) {
ex.printStackTrace(System.out);
}
// Should cause an error
System.out.println("\nCreating tuple with wrong # of arguments: ");
try {
final Tuple terror = emptyTupleType.createTuple(1);
System.out.println("Creating this tuple should have failed: "+terror);
} catch (IllegalArgumentException ex) {
ex.printStackTrace(System.out);
}
// Should cause an error
System.out.println("\nGetting value as wrong type: ");
try {
final Tuple t9 = tripletTupleType.createTuple(9, "nine", 'i');
final String verror = t9.getNthValue(0);
System.out.println("Getting this value should have failed: "+verror);
} catch (ClassCastException ex) {
ex.printStackTrace(System.out);
}
}
}
===
Sample Run
===
t1 = [1, one, a]
t2 = [2, two, b]
t3 = [3.0, three, c]
tnull = [null, (null), null]
tempty = []
Creating tuple with wrong types:
java.lang.IllegalArgumentException: Expected value #1 ('2') of new Tuple to be class java.lang.String, not class java.lang.Integer
at com.stackoverflow.tuple.TupleTypeImpl.createTuple(TupleTypeImpl.java:32)
at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.java:37)
Creating tuple with wrong # of arguments:
java.lang.IllegalArgumentException: Expected 0 values, not 1 values
at com.stackoverflow.tuple.TupleTypeImpl.createTuple(TupleTypeImpl.java:22)
at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.java:46)
Getting value as wrong type:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.java:58)
Как это безопасно? Вы генерируете исключения времени выполнения вместо сообщения об ошибках типа во время компиляции.
Вы пытаетесь абстрагироваться от арности, которая (на данный момент) невозможна в статически типизированных языках, без потери безопасности типов.
Приложение:
Кортежи могут состоять из разнородных элементов (то есть элементов с различными типами). Поэтому обеспечение даже «безопасности типов rutime» для этого класса Tuple
невозможно. Клиенты класса несут ответственность за выполнение соответствующих бросков.
Это лучшее, что вы можете сделать в Java: strike> ( Редактировать: См. пост Брента для лучшей реализации Tuple
. (Это не не требует типов на стороне клиента.))
final class Tuple {
private final List<Object> elements;
public Tuple(final Object ... elements) {
this.elements = Arrays.asList(elements);
}
@Override
public String toString() {
return elements.toString();
}
//
// Override 'equals' and 'hashcode' here
//
public Object at(final int index) {
return elements.get(index);
}
}
Это самое простое и лучшее решение. Это похоже на то, как кортежи представлены в .NET. Это тщательно обходит стирание Java. Это сильно типизировано. Это не бросает исключения. Он очень прост в использовании.
public interface Tuple
{
int size();
}
public class Tuple2<T1,T2> implements Tuple
{
public final T1 item1;
public final T2 item2;
public Tuple2(
final T1 item_1,
final T2 item_2)
{
item1 = item_1;
item2 = item_2;
}
@Override
public int size()
{
return 2;
}
}
public class Tuple3<T1,T2,T3> implements Tuple
{
public final T1 item1;
public final T2 item2;
public final T3 item3;
public Tuple3(
final T1 item_1,
final T2 item_2,
final T3 item_3)
{
item1 = item_1;
item2 = item_2;
item3 = item_3;
}
@Override
public int size()
{
return 3;
}
}
Вы должны посмотреть на реализацию .NET в Tuple . Они безопасны во время компиляции.
Какова цель typeLock
? Разрешить кому-то помешать построить еще какие-нибудь из этих объектов? Эта часть не имеет особого смысла.
Почему вы хотите позволить кому-либо предотвратить дальнейшую реализацию ваших объектов? Если по какой-то причине это то, что вам нужно, вместо того, чтобы «блокировать» класс и создавать исключения, просто убедитесь, что путь к коду ... не создает больше объектов этого типа.
Какова цель статического lastTuple
, который устанавливается в качестве ссылки на последний экземпляр Tuple
? Это плохая практика, чтобы смешивать статические ссылки, как это.
Честно говоря, код довольно запутанный, хотя необходимость в этом классе сбивает с толку. Если бы это был какой-то код, который я просматривал в рабочей среде, я бы этого не допустил.
видел этот код в волновом проекте
public class Tuple<A> {
private final A[] elements;
public static <A> Tuple<A> of(A ... elements) {
return new Tuple<A>(elements);
}
public Tuple(A ... elements) {
this.elements = elements;
}
public A get(int index) {
return elements[index];
}
public int size() {
return elements.length;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || o.getClass() != this.getClass()) {
return false;
}
Tuple<A> o2 = (Tuple<A>) o;
return Arrays.equals(elements, o2.elements);
}
@Override
public int hashCode() {
return Arrays.hashCode(elements);
}
@Override
public String toString() {
return Arrays.toString(elements);
}
}
Вот действительно ужасная реализация с n-кортежами, в которой используются универсальные средства для проверки типов во время компиляции. Основной метод (предоставленный для демонстрационных целей) показывает, насколько ужасным было бы это использовать:
interface ITuple { }
/**
* Typed immutable arbitrary-length tuples implemented as a linked list.
*
* @param <A> Type of the first element of the tuple
* @param <D> Type of the rest of the tuple
*/
public class Tuple<A, D extends ITuple> implements ITuple {
/** Final element of a tuple, or the single no-element tuple. */
public static final TupleVoid END = new TupleVoid();
/** First element of tuple. */
public final A car;
/** Remainder of tuple. */
public final D cdr;
public Tuple(A car, D cdr) {
this.car = car;
this.cdr = cdr;
}
private static class TupleVoid implements ITuple { private TupleVoid() {} }
// Demo time!
public static void main(String[] args) {
Tuple<String, Tuple<Integer, Tuple<String, TupleVoid>>> triple =
new Tuple<String, Tuple<Integer, Tuple<String, TupleVoid>>>("one",
new Tuple<Integer, Tuple<String, TupleVoid>>(2,
new Tuple<String, TupleVoid>("three",
END)));
System.out.println(triple.car + "/" + triple.cdr.car + "/" + triple.cdr.cdr.car);
//: one/2/three
}
}
Если вы действительно заинтересованы в написании безопасных для типов контейнеров, посмотрите на дженерики:
public class Tuple<T> {
private final T[] arr;
public Tuple (T... contents) {
arr = contents; //not sure if this compiles??
}
// etc
public static final void main(String[] args) {
Tuple<String> stringTuple = new Tuple<String>("Hello", "World!");
Tuple<Integer> intTuple = new Tuple<Integer>(2010,9,4);
}
}